mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-15 01:13:30 +00:00
library/getopt.m:
library/getopt_io.m:
Fix an incorrect replacement named in an obsolete pragma.
NEWS.md:
Fix incorrect arities in some entries.
2303 lines
92 KiB
Mathematica
2303 lines
92 KiB
Mathematica
%---------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%---------------------------------------------------------------------------%
|
|
% Copyright (C) 1994-1999, 2001-2007, 2011 The University of Melbourne.
|
|
% Copyright (C) 2014-2018, 2020-2026 The Mercury team.
|
|
% This file is distributed under the terms specified in COPYING.LIB.
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% File: getopt.m.
|
|
% Authors: fjh, zs.
|
|
% Stability: medium.
|
|
%
|
|
% This module defines predicates that parse command line options.
|
|
%
|
|
% These predicates allow both short (single-character) options,
|
|
% which are preceded on command lines with a single dash, and GNU-style
|
|
% long options, which are preceded on command lines with a double dash.
|
|
% An argument starting with a single dash can specify more than one
|
|
% short option, so that e.g. `-cde' is equivalent to `-c -d -e', while
|
|
% each long option name must be in an argument of its own.
|
|
%
|
|
% The predicates in this module support the GNU extension of recognizing
|
|
% options anywhere in the command line, not just at its 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 long names
|
|
% respectively 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
|
|
% - string
|
|
% - maybe_int (which have a value of `no' or `yes(int)')
|
|
% - 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 (in the predicate variants that do I/O; see below)
|
|
%
|
|
% 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 (other than file_special)
|
|
% for which there is no handler, or for which the handler fails.
|
|
%
|
|
% 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.
|
|
%
|
|
% The reading of the file obviously requires doing I/O, which means that
|
|
% only the predicate variants that take an I/O state pair of arguments
|
|
% support file_special options. If a call to a predicate variant that
|
|
% does not take a pair of I/O states does nevertheless specify a file_special
|
|
% option, that predicate will report an error when processing a command line
|
|
% that contains that option.
|
|
%
|
|
% Boolean (i.e. bool or bool_special), maybe_int, maybe_string,
|
|
% maybe_string_special 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 `-',
|
|
% for example `-x-' will negate the `-x' option. Long options can be negated
|
|
% by preceding them with `--no-', for example `--no-foo' will negate
|
|
% the `--foo' option.
|
|
%
|
|
% Note that arguments following an option may be separated from the option by
|
|
% either whitespace or the equals character `=', so that 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
|
|
% arguments. This is sometimes needed to tell a program to treat strings
|
|
% that start with a dash as non-option arguments.
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- module getopt.
|
|
:- interface.
|
|
|
|
:- import_module bool.
|
|
:- import_module char.
|
|
:- import_module io.
|
|
:- import_module list.
|
|
:- import_module map.
|
|
:- import_module maybe.
|
|
:- import_module set.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- type short_option(OptionType) == (pred(char, OptionType)).
|
|
:- inst short_option == (pred(in, out) is semidet).
|
|
|
|
:- type long_option(OptionType) == (pred(string, OptionType)).
|
|
:- inst long_option == (pred(in, out) is semidet).
|
|
|
|
:- type option_default_value(OptionType) == (pred(OptionType, option_data)).
|
|
:- inst option_default_value_nondet == (pred(out, out) is nondet).
|
|
:- inst option_default_value_multi == (pred(out, out) is multi).
|
|
|
|
:- type option_default_value_map(OptionType) == map(OptionType, option_data).
|
|
|
|
:- type special_handler(OptionType) ==
|
|
(pred(OptionType, special_data,
|
|
option_table(OptionType), maybe_option_table(OptionType))).
|
|
:- inst special_handler ==
|
|
(pred(in, in, in, out) is semidet).
|
|
|
|
% The predicates below that process options, namely
|
|
%
|
|
% - process_options
|
|
% - process_options_io
|
|
% - process_options_track
|
|
% - process_options_track_io
|
|
%
|
|
% all take an argument of the option_ops type to tell them
|
|
%
|
|
% - what the default value of each option is;
|
|
% - what the short and long names of the options are;
|
|
% (see the comment at the top for a description of the distinction), and
|
|
% - if there are any special options, how they should be handled.
|
|
%
|
|
% The job of the option_ops type is to hold the three or four predicates
|
|
% used to categorize a set of options. (One of these may be replaced
|
|
% by a map). Their interfaces should be like these:
|
|
%
|
|
% % True if the character names a valid single-character short option.
|
|
% %
|
|
% :- pred short_option(char::in, option::out) is semidet.
|
|
%
|
|
% % True if the string names a valid long option.
|
|
% %
|
|
% :- pred long_option(string::in, option::out) is semidet.
|
|
%
|
|
% % Nondeterministically returns all the options with their
|
|
% % corresponding types and default values.
|
|
% % This predicate may also have determinism "nondet".
|
|
% %
|
|
% :- pred option_default(option::out, option_data::out) is multi.
|
|
%
|
|
% The option_default predicate may be replaced, in the option_ops values
|
|
% whose names include "default_map", with a data structure that maps
|
|
% each and every option to its default value. (This is normally useful
|
|
% if the caller itself needs the same data in map form.)
|
|
%
|
|
% % This predicate is invoked whenever getopt finds an option
|
|
% % designated as special (by either a short or long name),
|
|
% % 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 setting
|
|
% % optimization levels in compilers, which set many other options
|
|
% % at once.
|
|
% %
|
|
% :- pred special_handler(option::in, special_data::in,
|
|
% option_table::in, maybe_option_table(_)::out) is semidet.
|
|
%
|
|
% The six function symbols in the option_ops type differ in
|
|
%
|
|
% - whether they contain a special_handler or not, and
|
|
% - whether option default values are defined by a predicate or a map,
|
|
% - and if they are defined by a predicate, whether the determinism
|
|
% of that predicate is nondet or multi,
|
|
%
|
|
:- type option_ops(OptionType)
|
|
---> option_ops(
|
|
short_option(OptionType),
|
|
long_option(OptionType),
|
|
option_default_value(OptionType)
|
|
)
|
|
; option_ops(
|
|
short_option(OptionType),
|
|
long_option(OptionType),
|
|
option_default_value(OptionType),
|
|
special_handler(OptionType)
|
|
)
|
|
; option_ops_multi(
|
|
short_option(OptionType),
|
|
long_option(OptionType),
|
|
option_default_value(OptionType)
|
|
)
|
|
; option_ops_multi(
|
|
short_option(OptionType),
|
|
long_option(OptionType),
|
|
option_default_value(OptionType),
|
|
special_handler(OptionType)
|
|
)
|
|
; option_ops_default_map(
|
|
short_option(OptionType),
|
|
long_option(OptionType),
|
|
option_default_value_map(OptionType)
|
|
)
|
|
; option_ops_default_map(
|
|
short_option(OptionType),
|
|
long_option(OptionType),
|
|
option_default_value_map(OptionType),
|
|
special_handler(OptionType)
|
|
).
|
|
|
|
:- inst option_ops for option_ops/1
|
|
---> option_ops(
|
|
short_option,
|
|
long_option,
|
|
option_default_value_nondet
|
|
)
|
|
; option_ops(
|
|
short_option,
|
|
long_option,
|
|
option_default_value_nondet,
|
|
special_handler
|
|
)
|
|
; option_ops_multi(
|
|
short_option,
|
|
long_option,
|
|
option_default_value_multi
|
|
)
|
|
; option_ops_multi(
|
|
short_option,
|
|
long_option,
|
|
option_default_value_multi,
|
|
special_handler
|
|
)
|
|
; option_ops_default_map(
|
|
short_option,
|
|
long_option,
|
|
ground
|
|
)
|
|
; option_ops_default_map(
|
|
short_option,
|
|
long_option,
|
|
ground,
|
|
special_handler
|
|
).
|
|
|
|
%---------------------%
|
|
|
|
:- type special_handler_track(OptionType) ==
|
|
(pred(OptionType, special_data,
|
|
option_table(OptionType), maybe_option_table(OptionType),
|
|
set(OptionType))).
|
|
:- inst special_handler_track ==
|
|
(pred(in, in, in, out, out) is semidet).
|
|
|
|
% A version of the option_ops type for the process_options_track
|
|
% predicate and its process_options_track_io variant.
|
|
% Unlike the option_ops type, it does not contain a predicate
|
|
% for setting the initial default values of options, since
|
|
% process_options_track expects that to be done separately.
|
|
%
|
|
:- type option_ops_track(OptionType)
|
|
---> option_ops_track(
|
|
short_option(OptionType),
|
|
long_option(OptionType),
|
|
special_handler_track(OptionType)
|
|
).
|
|
|
|
:- inst option_ops_track for option_ops_track/1
|
|
---> option_ops_track(
|
|
short_option,
|
|
long_option,
|
|
special_handler_track
|
|
).
|
|
|
|
%---------------------%
|
|
|
|
:- type user_data_handler(OptionType, UserDataType) ==
|
|
(pred(OptionType, special_data,
|
|
option_table(OptionType), maybe_option_table(OptionType),
|
|
UserDataType, UserDataType)).
|
|
:- inst user_data_handler ==
|
|
(pred(in, in, in, out, in, out) is semidet).
|
|
|
|
% A version of the option_ops type for the process_options_userdata
|
|
% predicate and its process_options_userdata_io variant.
|
|
%
|
|
:- type option_ops_userdata(OptionType, UserDataType)
|
|
---> option_ops_userdata(
|
|
short_option(OptionType),
|
|
long_option(OptionType),
|
|
user_data_handler(OptionType, UserDataType)
|
|
).
|
|
|
|
:- inst option_ops_userdata for option_ops_userdata/2
|
|
---> option_ops_userdata(
|
|
short_option,
|
|
long_option,
|
|
user_data_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 argument
|
|
% identifies the option enumeration value; the second identifies
|
|
% the string that appeared on the command line for that option;
|
|
% 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_but_no_io(string)
|
|
% The option is a file_special option whose argument is the file
|
|
% named by the first argument, but the user has not given the
|
|
% predicate access to the I/O state.
|
|
|
|
; 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.
|
|
|
|
; file_special_circular_inclusion(string).
|
|
% The option is a file_special option whose argument is the file
|
|
% named by the argument. This file contained either a direct
|
|
% or an indirect reference to an option that called for its
|
|
% inclusion, which, if followed, would have resulted in
|
|
% an infinite loop of inclusions.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% process_options(OptionOps, Args, NonOptionArgs, Result):
|
|
% process_options(OptionOps, Args, OptionArgs, NonOptionArgs, Result):
|
|
% process_options_io(OptionOps, Args, NonOptionArgs,
|
|
% Result, !IO):
|
|
% process_options_io(OptionOps, Args, OptionArgs, NonOptionArgs,
|
|
% Result, !IO):
|
|
%
|
|
% These four predicates do effectively the same job, differing
|
|
% from each other in two minor ways.
|
|
%
|
|
% The job they all do is scanning through Args looking for options.
|
|
% The various fields of the OptionOps structure specify the names
|
|
% (both short and long) of the options to look for, as well as their
|
|
% default values, and possibly the handler for the special options.
|
|
% The structure of the OptionOps argument is documented above,
|
|
% at the definition of the option_ops type.
|
|
%
|
|
% All these predicates place all the non-option arguments in
|
|
% 'NonOptionArgs', and the predicates that have an OptionArgs argument
|
|
% place the option arguments there. (While some callers will want
|
|
% the arguments contain the options, other callers will not, considering
|
|
% that the only information they want from them is that contained in
|
|
% the option table.)
|
|
%
|
|
% If they find a problem, such as an unknown option name, an option
|
|
% being given an argument of the wrong type, or the failure of the handler
|
|
% for a special option, all the predicates will put into Result
|
|
% an error() wrapped around an error code. That error code can be turned
|
|
% into an error message using the option_error_to_string function below.
|
|
%
|
|
% If they do not find a problem, all these predicates will place into
|
|
% Result an ok() wrapped around an option table, which maps each option
|
|
% to its final value. Unless updated by an option in Args, this will be
|
|
% its default value.
|
|
%
|
|
% The predicate versions whose names end in `io' take a pair of I/O state
|
|
% arguments. This is so that they can handle file_special options, which
|
|
% require reading a named file, and treating the file's contents as
|
|
% specifying additional options. The predicate versions whose names
|
|
% do not end in `io' cannot do I/O, and will report an error if they
|
|
% encounter a file_special option.
|
|
%
|
|
:- pred process_options(option_ops(OptionType)::in(option_ops),
|
|
list(string)::in, list(string)::out,
|
|
maybe_option_table_se(OptionType)::out) is det.
|
|
:- pred process_options(option_ops(OptionType)::in(option_ops),
|
|
list(string)::in, list(string)::out, list(string)::out,
|
|
maybe_option_table_se(OptionType)::out) is det.
|
|
:- pred process_options_io(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_io(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.
|
|
|
|
% process_options_track(OptionOps, Args, OptionArgs, NonOptionArgs,
|
|
% OptionTable0, Result, OptionsSet):
|
|
% process_options_track_io(OptionOps, Args, OptionArgs, NonOptionArgs,
|
|
% OptionTable0, Result, OptionsSet, !IO):
|
|
%
|
|
% These predicates differ from the non-track variants above
|
|
% in only two respects.
|
|
%
|
|
% First, they expect the caller to supply an argument containing
|
|
% the initial contents of the option table, instead of calling
|
|
% the initialization predicate themselves. This allows a program
|
|
% to initialize the option table just once (using either the
|
|
% init_option_table or the init_option_table_multi predicate below),
|
|
% but then call process_options_track or process_options_track_io
|
|
% several times, with different sets of arguments, perhaps obtained
|
|
% from different sources (such as command line, configuration file,
|
|
% and so on).
|
|
%
|
|
% Second, each call to one of these predicates returns the set of options
|
|
% that were set by that call. This helps with the same objective.
|
|
% For example, the caller can tell whether an option was set from
|
|
% a configuration file, the command line, both, or neither.
|
|
%
|
|
:- 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_se(OptionType)::out,
|
|
set(OptionType)::out) is det.
|
|
:- pred process_options_track_io(
|
|
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.
|
|
|
|
% process_options_userdata(OptionOps, Args, OptionArgs, NonOptionArgs,
|
|
% MaybeError, OptionsSet, !OptionTable, !UserData):
|
|
% process_options_userdata_io(OptionOps, Args, OptionArgs, NonOptionArgs,
|
|
% MaybeError, OptionsSet, !OptionTable, !UserData, !IO):
|
|
%
|
|
% These predicates are similar to the track predicates above, but differ
|
|
% in two ways.
|
|
%
|
|
% - They also thread a piece of state of a user-specified "userdata" type
|
|
% through all the handlers of special options, so that each
|
|
% special handler can read from and/or write to this state.
|
|
% Amongst other things, this can be used by the caller to recover
|
|
% the *sequence* in which special options are specified,
|
|
% information that is not present in the (orderless) set
|
|
% of specified options.
|
|
%
|
|
% - Even if they find an error, they return the option table as it was
|
|
% just before it found the error. This option table will reflect
|
|
% all the previous options that could be correctly processed.
|
|
%
|
|
:- pred process_options_userdata(
|
|
option_ops_userdata(OptionType, UserDataType)::in(option_ops_userdata),
|
|
list(string)::in, list(string)::out, list(string)::out,
|
|
maybe(option_error(OptionType))::out, set(OptionType)::out,
|
|
option_table(OptionType)::in, option_table(OptionType)::out,
|
|
UserDataType::in, UserDataType::out) is det.
|
|
:- pred process_options_userdata_io(
|
|
option_ops_userdata(OptionType, UserDataType)::in(option_ops_userdata),
|
|
list(string)::in, list(string)::out, list(string)::out,
|
|
maybe(option_error(OptionType))::out, set(OptionType)::out,
|
|
option_table(OptionType)::in, option_table(OptionType)::out,
|
|
UserDataType::in, UserDataType::out, io::di, io::uo) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% init_option_table(InitPred, OptionTable):
|
|
% init_option_table_multi(InitPred, OptionTable):
|
|
%
|
|
% Create an initial option table that maps each option to the default
|
|
% value specified for it by InitPred.
|
|
%
|
|
:- 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.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Each value of this type specifies
|
|
%
|
|
% - the identity of an option (in its first argument),
|
|
% - the string on the command line setting that option
|
|
% (in its second argument), and, if the option takes a value,
|
|
% - the value of the option (in its third argument).
|
|
%
|
|
% "special" options have no third argument, because they have
|
|
% no associated value.
|
|
%
|
|
% Each occurrence of an accumulating option adds only one string
|
|
% to the option's value, which is why ov_accumulating has one string.
|
|
% Options that reset an accumulating option to the empty list
|
|
% obviously have no associated value.
|
|
:- type option_value(OptionType)
|
|
---> ov_bool(OptionType, string, bool)
|
|
; ov_int(OptionType, string, int)
|
|
; ov_string(OptionType, string, string)
|
|
; ov_maybe_int(OptionType, string, maybe(int))
|
|
; ov_maybe_string(OptionType, string, maybe(string))
|
|
; ov_accumulating(OptionType, string, string)
|
|
; ov_accumulating_reset(OptionType, string)
|
|
; ov_special(OptionType, string)
|
|
; ov_bool_special(OptionType, string, bool)
|
|
; ov_int_special(OptionType, string, int)
|
|
; ov_string_special(OptionType, string, string)
|
|
; ov_maybe_string_special(OptionType, string, maybe(string))
|
|
; ov_file_special(OptionType, string, string).
|
|
|
|
:- type maybe_option_error(OptionType)
|
|
---> no_option_error
|
|
; found_option_error(option_error(OptionType)).
|
|
|
|
% recognize_options(ShortOptionPred, LongOptionPred, OptionTable,
|
|
% Args, MaybeError, OptionValues, OptionArgs, NonOptionArgs):
|
|
% recognize_all_options(ShortOptionPred, LongOptionPred, OptionTable,
|
|
% Args, Errors, OptionValues, OptionArgs, NonOptionArgs):
|
|
%
|
|
% Given Args, which is a list of command line arguments,
|
|
%
|
|
% - classify them into arguments that are and are not option args,
|
|
% returning them as OptionArgs and NonOptionArgs respectively,
|
|
%
|
|
% - use the ShortOptionPred and LongOptionPred predicates
|
|
% to figure out which user-defined options the OptionArgs refer to,
|
|
%
|
|
% - use OptionTable to figure out what kind of value, if any,
|
|
% each of those user-defined options has as its argument,
|
|
%
|
|
% - find those arguments and convert them to the expected type,
|
|
% and return them in OptionValues.
|
|
%
|
|
% The two predicates differ in what they do when they find an error.
|
|
%
|
|
% - When it finds an error, recognize_options stops processing Args,
|
|
% and returns a description of the error in MaybeError. Any arguments
|
|
% after the error will not be included in any of OptionValues,
|
|
% OptionsArgs, or NonOptionArgs.
|
|
%
|
|
% - When it finds an error, recognize_all_options keeps processing Args,
|
|
% looking for more errors. It can therefore return more than one error
|
|
% in Errors. Any arguments between and after errors *will* be included
|
|
% either in OptionValues and OptionArgs, or in NonOptionArgs.
|
|
% Note though it is possible for recognize_all_options to mistake
|
|
% an argument that was intended to be an argument of a previous
|
|
% option to be something else (either an option in its own right, or
|
|
% a nonoption argument) if the previous argument is not recognizable
|
|
% as an option that has an argument because of e.g. a typo in its name.
|
|
%
|
|
% recognize_options indicates the lack of any errors by returning
|
|
% no_option_error in MaybeError. recognize_all_options indicates the same
|
|
% by returning the empty list as Errors.
|
|
%
|
|
% Note that unlike the process_options_... predicates above,
|
|
% these predicates do *not* update the option table in any way.
|
|
% They also simply return file_special options in OptionValues;
|
|
% they do not process them. That processing can be done by
|
|
% the expand_file_specials predicate below.
|
|
%
|
|
:- pred recognize_options(short_option(OptionType)::in(short_option),
|
|
long_option(OptionType)::in(long_option), option_table(OptionType)::in,
|
|
list(string)::in, maybe_option_error(OptionType)::out,
|
|
list(option_value(OptionType))::out,
|
|
list(string)::out, list(string)::out) is det.
|
|
:- pred recognize_all_options(short_option(OptionType)::in(short_option),
|
|
long_option(OptionType)::in(long_option), option_table(OptionType)::in,
|
|
list(string)::in,
|
|
list(option_error(OptionType))::out, list(option_value(OptionType))::out,
|
|
list(string)::out, list(string)::out) is det.
|
|
|
|
% record_arguments(ShortOptionPred, LongOptionPred, OptionTable,
|
|
% Args, NonOptionArgs, OptionArgs, MaybeError, OptionValues):
|
|
%
|
|
% This predicate is an obsolete synonym for
|
|
%
|
|
% recognize_options(ShortOptionPred, LongOptionPred, OptionTable,
|
|
% Args, MaybeError, OptionValues, OptionArgs, NonOptionArgs).
|
|
%
|
|
:- pred record_arguments(short_option(OptionType)::in(short_option),
|
|
long_option(OptionType)::in(long_option), option_table(OptionType)::in,
|
|
list(string)::in, list(string)::out, list(string)::out,
|
|
maybe_option_error(OptionType)::out,
|
|
list(option_value(OptionType))::out) is det.
|
|
:- pragma obsolete(pred(record_arguments/8), [recognize_options/8]).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% An inst that lists all the function symbols of the option_value type
|
|
% *except* ov_file_special.
|
|
:- inst non_file_special for option_value/1
|
|
---> ov_bool(ground, ground, ground)
|
|
; ov_int(ground, ground, ground)
|
|
; ov_string(ground, ground, ground)
|
|
; ov_maybe_int(ground, ground, ground)
|
|
; ov_maybe_string(ground, ground, ground)
|
|
; ov_accumulating(ground, ground, ground)
|
|
; ov_accumulating_reset(ground, ground)
|
|
; ov_special(ground, ground)
|
|
; ov_bool_special(ground, ground, ground)
|
|
; ov_int_special(ground, ground, ground)
|
|
; ov_string_special(ground, ground, ground)
|
|
; ov_maybe_string_special(ground, ground, ground).
|
|
|
|
% expand_file_specials(ShortOptionPred, LongOptionPred, OptionTable,
|
|
% OptionValues, MaybeError, NonFileSpecialOptionValues, !MaybeIO):
|
|
%
|
|
% Given a list of OptionValues as generated for example by
|
|
% recognize_options, replace each ov_file_special option value in that list
|
|
% with the option values in the file named by that option.
|
|
% If there are any errors, return a description of one of them
|
|
% in MaybeError; otherwise, return the fully expanded list of options
|
|
% in NonFileSpecialOptionValues, and set MaybeError to no_option_error.
|
|
%
|
|
% The ShortOptionPred, LongOptionPred and OptionTable arguments
|
|
% play the same role as in recognize_options, since expand_file_specials
|
|
% must of course recognize all the options in files named by
|
|
% ov_file_special option values.
|
|
%
|
|
:- pred expand_file_specials(short_option(OptionType)::in(short_option),
|
|
long_option(OptionType)::in(long_option), option_table(OptionType)::in,
|
|
list(option_value(OptionType))::in, maybe_option_error(OptionType)::out,
|
|
list(option_value(OptionType))::out(list_skel(non_file_special)),
|
|
io::di, io::uo) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% The following functions and predicates search the option table
|
|
% for an option of the specified kind. If the option is not in the table,
|
|
% they throw an exception.
|
|
|
|
:- func lookup_bool_option(option_table(Option), Option) = bool.
|
|
:- pred lookup_bool_option(option_table(Option)::in, Option::in,
|
|
bool::out) is det.
|
|
|
|
:- func lookup_int_option(option_table(Option), Option) = int.
|
|
:- pred lookup_int_option(option_table(Option)::in, Option::in,
|
|
int::out) is det.
|
|
|
|
:- func lookup_string_option(option_table(Option), Option) = string.
|
|
:- pred lookup_string_option(option_table(Option)::in, Option::in,
|
|
string::out) is det.
|
|
|
|
:- func lookup_maybe_int_option(option_table(Option), Option) = maybe(int).
|
|
:- pred lookup_maybe_int_option(option_table(Option)::in, Option::in,
|
|
maybe(int)::out) is det.
|
|
|
|
:- func lookup_maybe_string_option(option_table(Option), Option) =
|
|
maybe(string).
|
|
:- pred lookup_maybe_string_option(option_table(Option)::in, Option::in,
|
|
maybe(string)::out) is det.
|
|
|
|
:- func lookup_accumulating_option(option_table(Option), Option) =
|
|
list(string).
|
|
:- pred lookup_accumulating_option(option_table(Option)::in, Option::in,
|
|
list(string)::out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Convert the structured representation of an error
|
|
% to an error message.
|
|
%
|
|
:- func option_error_to_string(option_error(OptionType)) = string.
|
|
|
|
%---------------------%
|
|
|
|
% If the argument represents an error, then convert that error from
|
|
% the structured representation to an error message.
|
|
%
|
|
:- func convert_to_maybe_option_table(maybe_option_table_se(OptionType))
|
|
= maybe_option_table(OptionType).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module cord.
|
|
:- import_module pair.
|
|
:- import_module require.
|
|
:- import_module set_tree234.
|
|
:- import_module solutions.
|
|
:- import_module string.
|
|
:- import_module unit.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- type option_ops_special(OptionType, UserDataType)
|
|
---> none
|
|
; notrack(
|
|
pred(OptionType, special_data,
|
|
option_table(OptionType),
|
|
maybe_option_table(OptionType))
|
|
)
|
|
; track(
|
|
pred(OptionType, special_data,
|
|
option_table(OptionType),
|
|
maybe_option_table(OptionType),
|
|
set(OptionType))
|
|
)
|
|
; userdata(
|
|
pred(OptionType, special_data,
|
|
option_table(OptionType),
|
|
maybe_option_table(OptionType),
|
|
UserDataType, UserDataType)
|
|
).
|
|
|
|
:- inst option_ops_special for option_ops_special/2
|
|
---> none
|
|
; notrack(pred(in, in, in, out) is semidet)
|
|
; track(pred(in, in, in, out, out) is semidet)
|
|
; userdata(pred(in, in, in, out, in, out) is semidet).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% This type records the result of attempting the read the contents
|
|
% of a named file.
|
|
:- type read_file_contents_result
|
|
---> read_success(string)
|
|
% The read succeeded, and the string is the entire contents
|
|
% of the named file.
|
|
; read_failure_open(io.error)
|
|
% The read failed, as the file open failed with this error.
|
|
; read_failure_read(io.error)
|
|
% The read failed, as the file read failed with this error.
|
|
; read_failure_no_io.
|
|
% The read failed, as we have no access to I/O.
|
|
|
|
:- typeclass read_file_contents(MaybeIO) where [
|
|
pred read_file_contents(string::in, read_file_contents_result::out,
|
|
MaybeIO::di, MaybeIO::uo) is det
|
|
].
|
|
|
|
:- instance read_file_contents(io) where [
|
|
pred(read_file_contents/4) is do_read_file_contents
|
|
].
|
|
|
|
:- instance read_file_contents(unit) where [
|
|
pred(read_file_contents/4) is read_file_contents_no_io
|
|
].
|
|
|
|
:- pred do_read_file_contents(string::in, read_file_contents_result::out,
|
|
io::di, io::uo) is det.
|
|
|
|
do_read_file_contents(FileName, Result, !IO) :-
|
|
io.open_input(FileName, OpenResult, !IO),
|
|
(
|
|
OpenResult = ok(FileStream),
|
|
io.read_file_as_string(FileStream, ReadResult, !IO),
|
|
(
|
|
ReadResult = ok(Contents),
|
|
Result = read_success(Contents)
|
|
;
|
|
ReadResult = error(_, IO_Error),
|
|
Result = read_failure_read(IO_Error)
|
|
),
|
|
io.close_input(FileStream, !IO)
|
|
;
|
|
OpenResult = error(IO_Error),
|
|
Result = read_failure_open(IO_Error)
|
|
).
|
|
|
|
:- pred read_file_contents_no_io(string::in, read_file_contents_result::out,
|
|
unit::di, unit::uo) is det.
|
|
|
|
read_file_contents_no_io(_FileName, Result, Unit, Unit) :-
|
|
Result = read_failure_no_io.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
process_options(OptionOps, Args, NonOptionArgs, Result) :-
|
|
process_options(OptionOps, Args, _OptionArgs, NonOptionArgs, Result).
|
|
|
|
process_options(OptionOps, Args, OptionArgs, NonOptionArgs, Result) :-
|
|
option_ops_to_internal_and_option_table(OptionOps, Short, Long, Special,
|
|
OptionTable0),
|
|
process_arguments(Short, Long, Special, Args, NonOptionArgs, OptionArgs,
|
|
MaybeOptionError, OptionTable0, OptionTable,
|
|
set.init, _OptionsSet, unit, _, unit, _),
|
|
return_option_table_if_ok(MaybeOptionError, OptionTable, Result).
|
|
|
|
process_options_io(OptionOps, Args, NonOptionArgs, Result, !IO) :-
|
|
process_options_io(OptionOps, Args, _OptionArgs, NonOptionArgs,
|
|
Result, !IO).
|
|
|
|
process_options_io(OptionOps, Args, OptionArgs, NonOptionArgs, Result, !IO) :-
|
|
option_ops_to_internal_and_option_table(OptionOps, Short, Long, Special,
|
|
OptionTable0),
|
|
process_arguments(Short, Long, Special, Args, NonOptionArgs, OptionArgs,
|
|
MaybeOptionError, OptionTable0, OptionTable,
|
|
set.init, _OptionsSet, unit, _, !IO),
|
|
return_option_table_if_ok(MaybeOptionError, OptionTable, Result).
|
|
|
|
process_options_track(OptionOps, Args, OptionArgs, NonOptionArgs,
|
|
OptionTable0, Result, OptionsSet) :-
|
|
OptionOps = option_ops_track(Short, Long, Special),
|
|
process_arguments(Short, Long, track(Special),
|
|
Args, NonOptionArgs, OptionArgs, MaybeOptionError,
|
|
OptionTable0, OptionTable, set.init, OptionsSet, unit, _, unit, _),
|
|
return_option_table_if_ok(MaybeOptionError, OptionTable, Result).
|
|
|
|
process_options_track_io(OptionOps, Args, OptionArgs, NonOptionArgs,
|
|
OptionTable0, Result, OptionsSet, !IO) :-
|
|
OptionOps = option_ops_track(Short, Long, Special),
|
|
process_arguments(Short, Long, track(Special),
|
|
Args, NonOptionArgs, OptionArgs, MaybeOptionError,
|
|
OptionTable0, OptionTable, set.init, OptionsSet, unit, _, !IO),
|
|
return_option_table_if_ok(MaybeOptionError, OptionTable, Result).
|
|
|
|
process_options_userdata(OptionOps, Args, OptionArgs, NonOptionArgs,
|
|
MaybeError, OptionsSet, !OptionTable, !UserData) :-
|
|
OptionOps = option_ops_userdata(Short, Long, Special),
|
|
process_arguments(Short, Long, userdata(Special),
|
|
Args, NonOptionArgs, OptionArgs, MaybeOptionError,
|
|
!OptionTable, set.init, OptionsSet, !UserData, unit, _),
|
|
return_maybe_option_error(MaybeOptionError, MaybeError).
|
|
|
|
process_options_userdata_io(OptionOps, Args, OptionArgs, NonOptionArgs,
|
|
MaybeError, OptionsSet, !OptionTable, !UserData, !IO) :-
|
|
OptionOps = option_ops_userdata(Short, Long, Special),
|
|
process_arguments(Short, Long, userdata(Special),
|
|
Args, NonOptionArgs, OptionArgs, MaybeOptionError,
|
|
!OptionTable, set.init, OptionsSet, !UserData, !IO),
|
|
return_maybe_option_error(MaybeOptionError, MaybeError).
|
|
|
|
%---------------------%
|
|
|
|
:- pred option_ops_to_internal_and_option_table(
|
|
option_ops(OptionType)::in(option_ops),
|
|
short_option(OptionType)::out(short_option),
|
|
long_option(OptionType)::out(long_option),
|
|
option_ops_special(OptionType, UserDataType)::out(option_ops_special),
|
|
option_table(OptionType)::out) is det.
|
|
|
|
option_ops_to_internal_and_option_table(OptionOps, Short, Long, MaybeSpecial,
|
|
OptionTable0) :-
|
|
(
|
|
OptionOps = option_ops(Short, Long, Defaults),
|
|
MaybeSpecial = none,
|
|
init_option_table(Defaults, OptionTable0)
|
|
;
|
|
OptionOps = option_ops(Short, Long, Defaults, Special),
|
|
MaybeSpecial = notrack(Special),
|
|
init_option_table(Defaults, OptionTable0)
|
|
;
|
|
OptionOps = option_ops_multi(Short, Long, Defaults),
|
|
MaybeSpecial = none,
|
|
init_option_table_multi(Defaults, OptionTable0)
|
|
;
|
|
OptionOps = option_ops_multi(Short, Long, Defaults, Special),
|
|
MaybeSpecial = notrack(Special),
|
|
init_option_table_multi(Defaults, OptionTable0)
|
|
;
|
|
OptionOps = option_ops_default_map(Short, Long, OptionTable0),
|
|
MaybeSpecial = none
|
|
;
|
|
OptionOps = option_ops_default_map(Short, Long, OptionTable0, Special),
|
|
MaybeSpecial = notrack(Special)
|
|
).
|
|
|
|
:- pred return_option_table_if_ok(maybe_option_error(OptionType)::in,
|
|
option_table(OptionType)::in,
|
|
maybe_option_table_se(OptionType)::out) is det.
|
|
|
|
return_option_table_if_ok(MaybeOptionError, OptionTable, Result) :-
|
|
(
|
|
MaybeOptionError = no_option_error,
|
|
Result = ok(OptionTable)
|
|
;
|
|
MaybeOptionError = found_option_error(Error),
|
|
Result = error(Error)
|
|
).
|
|
|
|
:- pred return_maybe_option_error(maybe_option_error(OptionType)::in,
|
|
maybe(option_error(OptionType))::out) is det.
|
|
|
|
return_maybe_option_error(no_option_error, no).
|
|
return_maybe_option_error(found_option_error(Error), yes(Error)).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
init_option_table(OptionDefaultsPred, OptionTable) :-
|
|
solutions(
|
|
( pred(OptionDataPair::out) is nondet :-
|
|
OptionDefaultsPred(Flag, OptionData),
|
|
OptionDataPair = Flag - OptionData
|
|
), OptionDefaultsList),
|
|
map.from_sorted_assoc_list(OptionDefaultsList, OptionTable).
|
|
|
|
init_option_table_multi(OptionDefaultsPred, OptionTable) :-
|
|
solutions(
|
|
( pred(OptionDataPair::out) is multi :-
|
|
OptionDefaultsPred(Flag, OptionData),
|
|
OptionDataPair = Flag - OptionData
|
|
), OptionDefaultsList),
|
|
map.from_sorted_assoc_list(OptionDefaultsList, OptionTable).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred process_arguments(
|
|
short_option(OptionType)::in(short_option),
|
|
long_option(OptionType)::in(long_option),
|
|
option_ops_special(OptionType, UserDataType)::in(option_ops_special),
|
|
list(string)::in, list(string)::out, list(string)::out,
|
|
maybe_option_error(OptionType)::out,
|
|
option_table(OptionType)::in, option_table(OptionType)::out,
|
|
set(OptionType)::in, set(OptionType)::out,
|
|
UserDataType::in, UserDataType::out, MaybeIO::di, MaybeIO::uo) is det
|
|
<= read_file_contents(MaybeIO).
|
|
|
|
process_arguments(ShortOptionPred, LongOptionPred, SpecialHandler,
|
|
Args, NonOptionArgs, OptionArgs, MaybeError,
|
|
!OptionTable, !OptionsSet, !UserData, !MaybeIO) :-
|
|
% We process the argument list in three phases.
|
|
%
|
|
% - In phase 1, recognize_options loops over all the arguments, and
|
|
% separates them into the option arguments and the non-option arguments,
|
|
% recognizing and recording the options (and if relevant, their values)
|
|
% in option arguments.
|
|
%
|
|
% - In phase 2, expand_file_specials loops over all the recorded option
|
|
% values, looking for file_special options. When it finds one, it
|
|
% replaces that file_special option with the option sequence in the
|
|
% file named in the option's argument, *after* invoking itself on that
|
|
% option sequence. This expands out *all* file_special options,
|
|
% regardless of how deeply they may be nested inside other
|
|
% file_special options.
|
|
%
|
|
% - In phase 3, we process the list of recorded option values as expanded
|
|
% out by expand_file_specials, updating the option table and !UserData
|
|
% (if applicable), and filling in !OptionsSet, as we go.
|
|
%
|
|
% All three phases may find and report errors. At the moment,
|
|
% we go on to second and third phase only if the earlier phases
|
|
% did not report any errors, but this is mostly because the signatures
|
|
% of the exported predicates of this module have long had room to report
|
|
% only *one* error. If we could report more than one error, we could
|
|
% run every phase on the outputs of the earlier phases, even if those
|
|
% outputs are only partial due to earlier failures.
|
|
%
|
|
recognize_options(ShortOptionPred, LongOptionPred, !.OptionTable,
|
|
Args, MaybeRecordError, OptionValues, OptionArgs, NonOptionArgs),
|
|
(
|
|
MaybeRecordError = no_option_error,
|
|
expand_file_specials_check(ShortOptionPred, LongOptionPred,
|
|
!.OptionTable, OptionValues, set.init, MaybeExpandError,
|
|
NFSOptionValues, !MaybeIO),
|
|
(
|
|
MaybeExpandError = no_option_error,
|
|
% Inserting into a set is O(N) in the size of the set, while
|
|
% inserting into a set_tree234 is O(log N). This gain more than
|
|
% pays for the two conversions we do here.
|
|
%
|
|
% XXX We could eliminate the conversions at the cost of doing
|
|
% s/set/set_tree234/ on the arg list of the exported predicates
|
|
% that call us. Should we make this breaking change?
|
|
OptionsSet0 = set_tree234.sorted_list_to_set(
|
|
set.to_sorted_list(!.OptionsSet)),
|
|
process_option_values(SpecialHandler, NFSOptionValues, MaybeError,
|
|
!OptionTable, OptionsSet0, OptionsSet, !UserData),
|
|
!:OptionsSet = set.sorted_list_to_set(
|
|
set_tree234.to_sorted_list(OptionsSet))
|
|
;
|
|
MaybeExpandError = found_option_error(_Error),
|
|
MaybeError = MaybeExpandError
|
|
)
|
|
;
|
|
MaybeRecordError = found_option_error(_Error),
|
|
MaybeError = MaybeRecordError
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
recognize_options(ShortOptionPred, LongOptionPred, OptionTable, Args,
|
|
MaybeError, OptionValues, OptionArgs, NonOptionArgs) :-
|
|
recognize_options_loop(ShortOptionPred, LongOptionPred, OptionTable,
|
|
Args, MaybeError, cord.init, OptionValuesCord,
|
|
cord.init, OptionArgsCord, cord.init, NonOptionArgsCord),
|
|
OptionValues = cord.to_list(OptionValuesCord),
|
|
OptionArgs = cord.list(OptionArgsCord),
|
|
NonOptionArgs = cord.list(NonOptionArgsCord).
|
|
|
|
recognize_all_options(ShortOptionPred, LongOptionPred, OptionTable, Args,
|
|
Errors, OptionValues, OptionArgs, NonOptionArgs) :-
|
|
record_all_arguments_loop(ShortOptionPred, LongOptionPred, OptionTable,
|
|
Args, cord.init, ErrorCord, cord.init, OptionValuesCord,
|
|
cord.init, OptionArgsCord, cord.init, NonOptionArgsCord),
|
|
Errors = cord.list(ErrorCord),
|
|
OptionValues = cord.to_list(OptionValuesCord),
|
|
OptionArgs = cord.list(OptionArgsCord),
|
|
NonOptionArgs = cord.list(NonOptionArgsCord).
|
|
|
|
record_arguments(ShortOptionPred, LongOptionPred, OptionTable, Args,
|
|
NonOptionArgs, OptionArgs, MaybeError, OptionValues) :-
|
|
recognize_options(ShortOptionPred, LongOptionPred, OptionTable, Args,
|
|
MaybeError, OptionValues, OptionArgs, NonOptionArgs).
|
|
|
|
:- pred recognize_options_loop(short_option(OptionType)::in(short_option),
|
|
long_option(OptionType)::in(long_option), option_table(OptionType)::in,
|
|
list(string)::in, maybe_option_error(OptionType)::out,
|
|
cord(option_value(OptionType))::in, cord(option_value(OptionType))::out,
|
|
cord(string)::in, cord(string)::out,
|
|
cord(string)::in, cord(string)::out) is det.
|
|
|
|
recognize_options_loop(_, _, _, [],
|
|
no_option_error, !OptionValues, !OptionArgs, !NonOptionArgs).
|
|
recognize_options_loop(ShortOptionPred, LongOptionPred, OptionTable,
|
|
CLArgs0, MaybeError, !OptionValues, !OptionArgs, !NonOptionArgs) :-
|
|
CLArgs0 = [_ | _],
|
|
recognize_option(ShortOptionPred, LongOptionPred, OptionTable,
|
|
CLArgs0, CLArgs1, Result),
|
|
(
|
|
(
|
|
(
|
|
Result = ror_long_option(CLArg, MaybeSepOptionCLArg, OV),
|
|
OVs = [OV]
|
|
;
|
|
Result = ror_short_options(CLArg, MaybeSepOptionCLArg, OVs)
|
|
),
|
|
cord.snoc(CLArg, !OptionArgs),
|
|
(
|
|
MaybeSepOptionCLArg = no_sep_arg
|
|
;
|
|
MaybeSepOptionCLArg = sep_arg(SepOptionCLArg),
|
|
cord.snoc(SepOptionCLArg, !OptionArgs)
|
|
),
|
|
cord.snoc_list(OVs, !OptionValues)
|
|
;
|
|
Result = ror_nonoption(CLArg),
|
|
cord.snoc(CLArg, !NonOptionArgs)
|
|
),
|
|
% As a GNU extension, even after we find a non-option argument,
|
|
% we keep searching for options.
|
|
recognize_options_loop(ShortOptionPred, LongOptionPred, OptionTable,
|
|
CLArgs1, MaybeError, !OptionValues, !OptionArgs, !NonOptionArgs)
|
|
;
|
|
Result = ror_error(Error),
|
|
MaybeError = found_option_error(Error)
|
|
;
|
|
Result = ror_end_of_options,
|
|
MaybeError = no_option_error
|
|
).
|
|
|
|
:- pred record_all_arguments_loop(short_option(OptionType)::in(short_option),
|
|
long_option(OptionType)::in(long_option), option_table(OptionType)::in,
|
|
list(string)::in,
|
|
cord(option_error(OptionType))::in, cord(option_error(OptionType))::out,
|
|
cord(option_value(OptionType))::in, cord(option_value(OptionType))::out,
|
|
cord(string)::in, cord(string)::out,
|
|
cord(string)::in, cord(string)::out) is det.
|
|
|
|
record_all_arguments_loop(_, _, _, [],
|
|
!Errors, !OptionValues, !OptionArgs, !NonOptionArgs).
|
|
record_all_arguments_loop(ShortOptionPred, LongOptionPred, OptionTable,
|
|
CLArgs0, !Errors, !OptionValues, !OptionArgs, !NonOptionArgs) :-
|
|
CLArgs0 = [_ | _],
|
|
recognize_option(ShortOptionPred, LongOptionPred, OptionTable,
|
|
CLArgs0, CLArgs1, Result),
|
|
(
|
|
(
|
|
(
|
|
Result = ror_long_option(CLArg, MaybeSepOptionCLArg, OV),
|
|
OVs = [OV]
|
|
;
|
|
Result = ror_short_options(CLArg, MaybeSepOptionCLArg, OVs)
|
|
),
|
|
cord.snoc(CLArg, !OptionArgs),
|
|
(
|
|
MaybeSepOptionCLArg = no_sep_arg
|
|
;
|
|
MaybeSepOptionCLArg = sep_arg(SepOptionCLArg),
|
|
cord.snoc(SepOptionCLArg, !OptionArgs)
|
|
),
|
|
cord.snoc_list(OVs, !OptionValues)
|
|
;
|
|
Result = ror_nonoption(CLArg),
|
|
cord.snoc(CLArg, !NonOptionArgs)
|
|
;
|
|
Result = ror_error(Error),
|
|
cord.snoc(Error, !Errors)
|
|
),
|
|
% As a GNU extension, even after we find a non-option argument,
|
|
% we keep searching for options.
|
|
record_all_arguments_loop(ShortOptionPred, LongOptionPred, OptionTable,
|
|
CLArgs1, !Errors, !OptionValues, !OptionArgs, !NonOptionArgs)
|
|
;
|
|
Result = ror_end_of_options
|
|
).
|
|
|
|
%---------------------%
|
|
|
|
:- type recognize_option_result(OptionType)
|
|
---> ror_long_option(
|
|
% This command line word represents a long option.
|
|
% If the option takes an argument, then
|
|
% - either this string has the form --optionname=VALUE,
|
|
% - or it contains just --optionname, and ...
|
|
string,
|
|
|
|
% ... VALUE is given by the next word on the command line,
|
|
% which is stored in this field.
|
|
maybe_sep_arg,
|
|
|
|
% The representation of the long option and its value.
|
|
option_value(OptionType)
|
|
)
|
|
; ror_short_options(
|
|
% This command line word represents one *or more*
|
|
% short options. If a short option has an argument, then
|
|
% the *rest* of the word, if any, becomes its argument,
|
|
% so only the last short option may have an argument.
|
|
% Given this fact, this word will always contain
|
|
% a string of the form -abcd. This may represent
|
|
% - the short option -a with argument "bcd";
|
|
% - the short option -a with no argument and
|
|
% the short option -b with argument "cd";
|
|
% - the short options -a and -b with no argument and
|
|
% the short option -c with argument "d";
|
|
% - the short options -a, -b and -c with no argument and
|
|
% the short option -d with the argument being given
|
|
% by the next field; or
|
|
% - the short options -a, -b, -c and -d with no argument.
|
|
string,
|
|
|
|
% In the second-to-last situation above, this field
|
|
% will contain the value of the last option's argument.
|
|
% In all the other situations, it will contain nothing.
|
|
maybe_sep_arg,
|
|
|
|
% The representation of the option kinds and their values.
|
|
% There will be at least one item in the list.
|
|
% (Using one_or_more to encode this invariant would be
|
|
% overkill, since the only thing we do with these fields
|
|
% is concatenate their values.)
|
|
list(option_value(OptionType))
|
|
)
|
|
; ror_nonoption(
|
|
% This command line word is neither an option, nor
|
|
% the argument of an option.
|
|
string
|
|
)
|
|
; ror_error(
|
|
% This command line word looks like it should represent
|
|
% one or more options, but it does not.
|
|
option_error(OptionType)
|
|
)
|
|
; ror_end_of_options.
|
|
% Either we have come to the end of the command line,
|
|
% or we have come to the word ("--") that tells us that
|
|
% we should stop processing any later words.
|
|
|
|
% Values of this type represent either the presence of an option's argument
|
|
% in a separate word of the argument list, or its absence.
|
|
:- type maybe_sep_arg
|
|
---> no_sep_arg
|
|
; sep_arg(string).
|
|
|
|
% Values of this type represent either the presence of an option's argument
|
|
% - either in a "=value" suffix after a long option,
|
|
% - or in a just plain "value suffix after one or more short options,
|
|
% or its absence.
|
|
:- type maybe_after_eq_arg
|
|
---> no_after_eq_arg
|
|
; after_eq_arg(string).
|
|
|
|
:- type value_or_error(OptionType) ==
|
|
maybe_error(option_value(OptionType), option_error(OptionType)).
|
|
|
|
% NOTE: In the following predicates, we talk about two kinds of arguments.
|
|
%
|
|
% - Command line arguments are the words that the command line consists of
|
|
% This code refers to these as CLArgs, CL being short for "command line".
|
|
%
|
|
% - Some options take arguments. Sometimes, an option's argument is in fact
|
|
% a command line argument, but sometimes, it is just *a part* of a command
|
|
% line argument. Specifically,
|
|
%
|
|
% - the argument of a long option may follow the name of the option
|
|
% in a command line argument of the form --long-option-name=OPTARG, and
|
|
%
|
|
% - the argument of a short option may follow the character identifying
|
|
% the option (e.g. z) in a command line argument of the form -xyzOPTARG.
|
|
|
|
:- pred recognize_option(short_option(OptionType)::in(short_option),
|
|
long_option(OptionType)::in(long_option), option_table(OptionType)::in,
|
|
list(string)::in, list(string)::out,
|
|
recognize_option_result(OptionType)::out) is det.
|
|
|
|
recognize_option(_ShortOptionPred, _LongOptionPred, _OptionTable,
|
|
[], [], ror_end_of_options).
|
|
recognize_option(ShortOptionPred, LongOptionPred, OptionTable,
|
|
[CLArg0 | CLArgs0], CLArgs, Result) :-
|
|
( if
|
|
% Test for an initial "--" just this once, instead of doing three
|
|
% times, one for each of the "--", "--no-.." and "--..."
|
|
% code paths below.
|
|
%
|
|
% We need the value of MaybeLongOption only on the third code path,
|
|
% but allocating memory for it on the first two code paths as well
|
|
% should not be a performance issue, since both of those paths
|
|
% are quite rare, the first structurally, the second statistically.
|
|
string.remove_prefix("--", CLArg0, MaybeLongOption)
|
|
then
|
|
( if MaybeLongOption = "" then
|
|
% "--" terminates option processing
|
|
CLArgs = CLArgs0,
|
|
Result = ror_end_of_options
|
|
else if string.remove_prefix("no-", MaybeLongOption, LongOption) then
|
|
% A negated long option. Negated options may not have arguments.
|
|
CLArgs = CLArgs0,
|
|
( if LongOptionPred(LongOption, Flag) then
|
|
OptName = "--" ++ LongOption,
|
|
record_negated_option(OptionTable, Flag, OptName, MaybeOption),
|
|
(
|
|
MaybeOption = ok(OptionValue),
|
|
Result = ror_long_option(CLArg0, no_sep_arg, OptionValue)
|
|
;
|
|
MaybeOption = error(Error),
|
|
Result = ror_error(Error)
|
|
)
|
|
else
|
|
Error = unrecognized_option(CLArg0),
|
|
Result = ror_error(Error)
|
|
)
|
|
else
|
|
% An unnegated long option. Unnegated options may have arguments.
|
|
( if string.sub_string_search(MaybeLongOption, "=", OptionLen) then
|
|
string.split(MaybeLongOption, OptionLen,
|
|
LongOption, EqualOptionArg),
|
|
OptName = "--" ++ LongOption,
|
|
( if string.first_char(EqualOptionArg, '=', OptionArg) then
|
|
MaybeAfterEqArg = after_eq_arg(OptionArg)
|
|
else
|
|
error("bad split of --longoption=arg")
|
|
)
|
|
else
|
|
LongOption = MaybeLongOption,
|
|
OptName = CLArg0,
|
|
MaybeAfterEqArg = no_after_eq_arg
|
|
),
|
|
( if LongOptionPred(LongOption, Flag) then
|
|
( if map.search(OptionTable, Flag, OptionData) then
|
|
record_unnegated_long_option(CLArg0, MaybeAfterEqArg,
|
|
Flag, OptName, OptionData, CLArgs0, CLArgs, Result)
|
|
else
|
|
CLArgs = CLArgs0,
|
|
Error = option_error(Flag, CLArg0, unknown_type),
|
|
Result = ror_error(Error)
|
|
)
|
|
else
|
|
CLArgs = CLArgs0,
|
|
Error = unrecognized_option(OptName),
|
|
Result = ror_error(Error)
|
|
)
|
|
)
|
|
else if
|
|
string.first_char(CLArg0, '-', ShortOptions),
|
|
ShortOptions \= ""
|
|
then
|
|
string.to_char_list(ShortOptions, ShortOptionsList),
|
|
( if ShortOptionsList = [SingleShortOpt, '-'] then
|
|
CLArgs = CLArgs0,
|
|
% A negated short option `-x-'.
|
|
% Negation may be applied only to a single option.
|
|
( if ShortOptionPred(SingleShortOpt, Flag) then
|
|
string.from_char_list(['-', SingleShortOpt], OptName),
|
|
record_negated_option(OptionTable, Flag, OptName, MaybeOption),
|
|
(
|
|
MaybeOption = ok(OptionValue),
|
|
Result = ror_short_options(CLArg0, no_sep_arg,
|
|
[OptionValue])
|
|
;
|
|
MaybeOption = error(Error),
|
|
Result = ror_error(Error)
|
|
)
|
|
else
|
|
Error = unrecognized_option("-" ++ ShortOptions),
|
|
Result = ror_error(Error)
|
|
)
|
|
else
|
|
% One or more unnegated short options `-xyz'.
|
|
% The initial `-' may be followed by several boolean options,
|
|
% or part of it may be the argument of an option.
|
|
% The first element of Args0 may also be an argument of an option.
|
|
record_unnegated_short_options(ShortOptionPred, OptionTable,
|
|
ShortOptionsList, CLArgs0, CLArgs,
|
|
MaybeShortCLArg, MaybeError, cord.init, OVsCord),
|
|
(
|
|
MaybeError = no_option_error,
|
|
OVs = cord.list(OVsCord),
|
|
% We have tested ShortOptions and it contains at least one
|
|
% short option, so in the absence of an error, there must be
|
|
% at least one item in OVs.
|
|
Result = ror_short_options(CLArg0, MaybeShortCLArg, OVs)
|
|
;
|
|
MaybeError = found_option_error(Error),
|
|
Result = ror_error(Error)
|
|
)
|
|
)
|
|
else
|
|
Result = ror_nonoption(CLArg0),
|
|
CLArgs = CLArgs0
|
|
).
|
|
|
|
%---------------------%
|
|
|
|
:- pred record_negated_option(option_table(OptionType)::in,
|
|
OptionType::in, string::in, value_or_error(OptionType)::out) is det.
|
|
|
|
record_negated_option(OptionTable, Flag, OptName, MaybeOptionValue) :-
|
|
( if map.search(OptionTable, Flag, OptionData) then
|
|
(
|
|
(
|
|
OptionData = bool(_),
|
|
OV = ov_bool(Flag, OptName, bool.no)
|
|
;
|
|
OptionData = maybe_int(_),
|
|
OV = ov_maybe_int(Flag, OptName, maybe.no)
|
|
;
|
|
OptionData = maybe_string(_),
|
|
OV = ov_maybe_string(Flag, OptName, maybe.no)
|
|
;
|
|
OptionData = accumulating(_),
|
|
OV = ov_accumulating_reset(Flag, OptName)
|
|
;
|
|
OptionData = bool_special,
|
|
OV = ov_bool_special(Flag, OptName, bool.no)
|
|
;
|
|
OptionData = maybe_string_special,
|
|
OV = ov_maybe_string_special(Flag, OptName, maybe.no)
|
|
),
|
|
MaybeOptionValue = ok(OV)
|
|
;
|
|
( OptionData = int_special
|
|
; OptionData = string_special
|
|
; OptionData = int(_)
|
|
; OptionData = string(_)
|
|
; OptionData = special
|
|
; OptionData = file_special
|
|
),
|
|
Error = option_error(Flag, OptName, cannot_negate),
|
|
MaybeOptionValue = error(Error)
|
|
)
|
|
else
|
|
Error = option_error(Flag, OptName, unknown_type),
|
|
MaybeOptionValue = error(Error)
|
|
).
|
|
|
|
%---------------------%
|
|
|
|
:- pred record_unnegated_long_option(string::in, maybe_after_eq_arg::in,
|
|
OptionType::in, string::in, option_data::in,
|
|
list(string)::in, list(string)::out,
|
|
recognize_option_result(OptionType)::out) is det.
|
|
|
|
record_unnegated_long_option(CLArg0, MaybeAfterEqArg,
|
|
Flag, OptName, OptionData, CLArgs0, CLArgs1, Result) :-
|
|
(
|
|
OptionData = special,
|
|
CLArgs1 = CLArgs0,
|
|
OV = ov_special(Flag, OptName),
|
|
Result0 = ror_long_option(CLArg0, no_sep_arg, OV),
|
|
report_any_unexpected_arg(MaybeAfterEqArg, Flag, OptName,
|
|
Result0, Result)
|
|
;
|
|
( OptionData = bool(_)
|
|
; OptionData = bool_special
|
|
),
|
|
CLArgs1 = CLArgs0,
|
|
record_option_bool(CLArg0, Flag, OptName, OptionData, yes, Result0),
|
|
report_any_unexpected_arg(MaybeAfterEqArg, Flag, OptName,
|
|
Result0, Result)
|
|
;
|
|
( OptionData = int(_)
|
|
; OptionData = maybe_int(_)
|
|
; OptionData = int_special
|
|
),
|
|
get_option_arg(CLArgs0, MaybeAfterEqArg, MaybeOptArg, CLArgs1),
|
|
(
|
|
MaybeOptArg = yes({OptArg, MaybeSepArg}),
|
|
record_option_int(CLArg0, MaybeSepArg,
|
|
Flag, OptName, OptionData, OptArg, Result)
|
|
;
|
|
MaybeOptArg = no,
|
|
Error = option_error(Flag, OptName, requires_argument),
|
|
Result = ror_error(Error)
|
|
)
|
|
;
|
|
( OptionData = string(_)
|
|
; OptionData = maybe_string(_)
|
|
; OptionData = accumulating(_)
|
|
; OptionData = string_special
|
|
; OptionData = maybe_string_special
|
|
; OptionData = file_special
|
|
),
|
|
get_option_arg(CLArgs0, MaybeAfterEqArg, MaybeOptArg, CLArgs1),
|
|
(
|
|
MaybeOptArg = yes({OptArg, MaybeSepArg}),
|
|
record_option_string(CLArg0, MaybeSepArg,
|
|
Flag, OptName, OptionData, OptArg, Result)
|
|
;
|
|
MaybeOptArg = no,
|
|
Error = option_error(Flag, OptName, requires_argument),
|
|
Result = ror_error(Error)
|
|
)
|
|
).
|
|
|
|
:- pred report_any_unexpected_arg(maybe_after_eq_arg::in,
|
|
OptionType::in, string::in, recognize_option_result(OptionType)::in,
|
|
recognize_option_result(OptionType)::out) is det.
|
|
|
|
report_any_unexpected_arg(MaybeAfterEqArg, Flag, OptName, Result0, Result) :-
|
|
% Note that there cannot ever be any unexpected *separate* arguments,
|
|
% because they would just be considered either the next option
|
|
% or the non-option argument. On the other hand, the VALUE in
|
|
% --optionname=VALUE does represent an unexpected argument
|
|
% if --optionname does not take an argument.
|
|
(
|
|
MaybeAfterEqArg = no_after_eq_arg,
|
|
Result = Result0
|
|
;
|
|
MaybeAfterEqArg = after_eq_arg(Arg),
|
|
ErrorReason = does_not_allow_argument(Arg),
|
|
Error = option_error(Flag, OptName, ErrorReason),
|
|
Result = ror_error(Error)
|
|
).
|
|
|
|
:- pred get_option_arg(list(string)::in, maybe_after_eq_arg::in,
|
|
maybe({string, maybe_sep_arg})::out, list(string)::out) is det.
|
|
|
|
get_option_arg(CLArgs0, MaybeAfterEqArg, MaybeOptArg, CLArgs1) :-
|
|
(
|
|
MaybeAfterEqArg = no_after_eq_arg,
|
|
(
|
|
CLArgs0 = [CLArg | CLArgs1],
|
|
MaybeOptArg = yes({CLArg, sep_arg(CLArg)})
|
|
;
|
|
CLArgs0 = [],
|
|
MaybeOptArg = no,
|
|
CLArgs1 = []
|
|
)
|
|
;
|
|
MaybeAfterEqArg = after_eq_arg(Arg),
|
|
MaybeOptArg = yes({Arg, no_sep_arg}),
|
|
CLArgs1 = CLArgs0
|
|
).
|
|
|
|
%---------------------%
|
|
|
|
:- pred record_unnegated_short_options(
|
|
short_option(OptionType)::in(short_option), option_table(OptionType)::in,
|
|
list(char)::in, list(string)::in, list(string)::out,
|
|
maybe_sep_arg::out, maybe_option_error(OptionType)::out,
|
|
cord(option_value(OptionType))::in, cord(option_value(OptionType))::out)
|
|
is det.
|
|
|
|
record_unnegated_short_options(_, _, [],
|
|
CLArgs0, CLArgs0, no_sep_arg, no_option_error, !OptionValues).
|
|
record_unnegated_short_options(ShortOptionPred, OptionTable, [Opt | Opts0],
|
|
CLArgs0, CLArgs, MaybeSepArg, MaybeError, !OptionValues) :-
|
|
( if ShortOptionPred(Opt, Flag) then
|
|
( if map.search(OptionTable, Flag, OptionData) then
|
|
string.from_char_list(['-', Opt], OptName),
|
|
(
|
|
OptionData = special,
|
|
option_none_to_ov(Flag, OptName, OptionData, OV),
|
|
cord.snoc(OV, !OptionValues),
|
|
record_unnegated_short_options(ShortOptionPred, OptionTable,
|
|
Opts0, CLArgs0, CLArgs, MaybeSepArg, MaybeError,
|
|
!OptionValues)
|
|
;
|
|
( OptionData = bool(_)
|
|
; OptionData = bool_special
|
|
),
|
|
option_bool_to_ov(Flag, OptName, OptionData, yes, OV),
|
|
cord.snoc(OV, !OptionValues),
|
|
record_unnegated_short_options(ShortOptionPred, OptionTable,
|
|
Opts0, CLArgs0, CLArgs, MaybeSepArg, MaybeError,
|
|
!OptionValues)
|
|
;
|
|
( OptionData = int(_)
|
|
; OptionData = maybe_int(_)
|
|
; OptionData = int_special
|
|
),
|
|
get_short_option_arg(Opts0, CLArgs0, OptArgPair, CLArgs),
|
|
OptArgPair = {OptArg, MaybeSepArg},
|
|
option_int_to_maybe_ov(Flag, OptName, OptionData, OptArg,
|
|
MaybeOV),
|
|
(
|
|
MaybeOV = yes(OV),
|
|
cord.snoc(OV, !OptionValues),
|
|
MaybeError = no_option_error
|
|
;
|
|
MaybeOV = no,
|
|
numeric_argument_error(Flag, OptName, OptArg, Error),
|
|
MaybeError = found_option_error(Error)
|
|
)
|
|
;
|
|
( OptionData = string(_)
|
|
; OptionData = maybe_string(_)
|
|
; OptionData = accumulating(_)
|
|
; OptionData = string_special
|
|
; OptionData = maybe_string_special
|
|
; OptionData = file_special
|
|
),
|
|
get_short_option_arg(Opts0, CLArgs0, OptArgPair, CLArgs),
|
|
OptArgPair = {OptArg, MaybeSepArg},
|
|
option_string_to_ov(Flag, OptName, OptionData, OptArg, OV),
|
|
cord.snoc(OV, !OptionValues),
|
|
MaybeError = no_option_error
|
|
)
|
|
else
|
|
CLArgs = CLArgs0,
|
|
MaybeSepArg = no_sep_arg,
|
|
string.char_to_string(Opt, OptString),
|
|
Error = option_error(Flag, "-" ++ OptString, unknown_type),
|
|
MaybeError = found_option_error(Error)
|
|
)
|
|
else
|
|
CLArgs = CLArgs0,
|
|
MaybeSepArg = no_sep_arg,
|
|
string.char_to_string(Opt, OptString),
|
|
Error = unrecognized_option("-" ++ OptString),
|
|
MaybeError = found_option_error(Error)
|
|
).
|
|
|
|
:- pred get_short_option_arg(list(char)::in, list(string)::in,
|
|
{string, maybe_sep_arg}::out, list(string)::out) is det.
|
|
|
|
get_short_option_arg(Opts, CLArgs0, OptArgPair, CLArgs) :-
|
|
( if
|
|
Opts = [],
|
|
CLArgs0 = [CLArg0 | CLArgs1]
|
|
then
|
|
OptArgPair = {CLArg0, sep_arg(CLArg0)},
|
|
CLArgs = CLArgs1
|
|
else
|
|
string.from_char_list(Opts, OptArg),
|
|
OptArgPair = {OptArg, no_sep_arg},
|
|
CLArgs = CLArgs0
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- inst option_data_none for option_data/0
|
|
---> special.
|
|
|
|
:- inst option_data_bool for option_data/0
|
|
---> bool(ground)
|
|
; bool_special.
|
|
|
|
:- inst option_data_int for option_data/0
|
|
---> int(ground)
|
|
; maybe_int(ground)
|
|
; int_special.
|
|
|
|
:- inst option_data_string for option_data/0
|
|
---> string(ground)
|
|
; maybe_string(ground)
|
|
; accumulating(ground)
|
|
; string_special
|
|
; maybe_string_special
|
|
; file_special.
|
|
|
|
%---------------------%
|
|
|
|
:- pred option_none_to_ov(
|
|
OptionType::in, string::in, option_data::in(option_data_none),
|
|
option_value(OptionType)::out) is det.
|
|
|
|
option_none_to_ov(Flag, OptName, OptionData, OV) :-
|
|
(
|
|
OptionData = special,
|
|
OV = ov_special(Flag, OptName)
|
|
).
|
|
|
|
:- pred record_option_bool(string::in,
|
|
OptionType::in, string::in, option_data::in(option_data_bool),
|
|
bool::in, recognize_option_result(OptionType)::out) is det.
|
|
|
|
record_option_bool(Arg0, Flag, OptName, OptionData,
|
|
BoolValue, Result) :-
|
|
option_bool_to_ov(Flag, OptName, OptionData, BoolValue, OV),
|
|
Result = ror_long_option(Arg0, no_sep_arg, OV).
|
|
|
|
:- pred option_bool_to_ov(
|
|
OptionType::in, string::in, option_data::in(option_data_bool),
|
|
bool::in, option_value(OptionType)::out) is det.
|
|
|
|
option_bool_to_ov(Flag, OptName, OptionData, BoolValue, OV) :-
|
|
(
|
|
OptionData = bool(_),
|
|
OV = ov_bool(Flag, OptName, BoolValue)
|
|
;
|
|
OptionData = bool_special,
|
|
OV = ov_bool_special(Flag, OptName, BoolValue)
|
|
).
|
|
|
|
:- pred record_option_int(string::in, maybe_sep_arg::in,
|
|
OptionType::in, string::in, option_data::in(option_data_int),
|
|
string::in, recognize_option_result(OptionType)::out) is det.
|
|
|
|
record_option_int(Arg0, MaybeSepArg, Flag, OptName, OptionData, Arg, Result) :-
|
|
option_int_to_maybe_ov(Flag, OptName, OptionData, Arg, MaybeOV),
|
|
(
|
|
MaybeOV = yes(OV),
|
|
Result = ror_long_option(Arg0, MaybeSepArg, OV)
|
|
;
|
|
MaybeOV = no,
|
|
numeric_argument_error(Flag, OptName, Arg, Error),
|
|
Result = ror_error(Error)
|
|
).
|
|
|
|
:- pred option_int_to_maybe_ov(
|
|
OptionType::in, string::in, option_data::in(option_data_int),
|
|
string::in, maybe(option_value(OptionType))::out) is det.
|
|
|
|
option_int_to_maybe_ov(Flag, OptName, OptionData, Arg, MaybeOV) :-
|
|
( if string.to_int(Arg, IntValue) then
|
|
(
|
|
OptionData = int(_),
|
|
OV = ov_int(Flag, OptName, IntValue)
|
|
;
|
|
OptionData = maybe_int(_),
|
|
OV = ov_maybe_int(Flag, OptName, yes(IntValue))
|
|
;
|
|
OptionData = int_special,
|
|
OV = ov_int_special(Flag, OptName, IntValue)
|
|
),
|
|
MaybeOV = yes(OV)
|
|
else
|
|
MaybeOV = no
|
|
).
|
|
|
|
:- pred record_option_string(string::in, maybe_sep_arg::in,
|
|
OptionType::in, string::in, option_data::in(option_data_string),
|
|
string::in, recognize_option_result(OptionType)::out) is det.
|
|
|
|
record_option_string(Arg0, MaybeSepArg, Flag, OptName, OptionData,
|
|
Arg, Result) :-
|
|
option_string_to_ov(Flag, OptName, OptionData, Arg, OV),
|
|
Result = ror_long_option(Arg0, MaybeSepArg, OV).
|
|
|
|
:- pred option_string_to_ov(
|
|
OptionType::in, string::in, option_data::in(option_data_string),
|
|
string::in, option_value(OptionType)::out) is det.
|
|
|
|
option_string_to_ov(Flag, OptName, OptionData, Arg, OV) :-
|
|
(
|
|
OptionData = string(_),
|
|
OV = ov_string(Flag, OptName, Arg)
|
|
;
|
|
OptionData = maybe_string(_),
|
|
OV = ov_maybe_string(Flag, OptName, yes(Arg))
|
|
;
|
|
OptionData = accumulating(_),
|
|
OV = ov_accumulating(Flag, OptName, Arg)
|
|
;
|
|
OptionData = string_special,
|
|
OV = ov_string_special(Flag, OptName, Arg)
|
|
;
|
|
OptionData = maybe_string_special,
|
|
OV = ov_maybe_string_special(Flag, OptName, yes(Arg))
|
|
;
|
|
OptionData = file_special,
|
|
OV = ov_file_special(Flag, OptName, Arg)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
expand_file_specials(ShortOptionPred, LongOptionPred, OptionTable,
|
|
OVs, MaybeError, NFSOVs, !MaybeIO) :-
|
|
expand_file_specials_check(ShortOptionPred, LongOptionPred, OptionTable,
|
|
OVs, set.init, MaybeError, NFSOVs, !MaybeIO).
|
|
|
|
:- pred expand_file_specials_check(short_option(OptionType)::in(short_option),
|
|
long_option(OptionType)::in(long_option),
|
|
option_table(OptionType)::in, list(option_value(OptionType))::in,
|
|
set(string)::in, maybe_option_error(OptionType)::out,
|
|
list(option_value(OptionType))::out(list_skel(non_file_special)),
|
|
MaybeIO::di, MaybeIO::uo) is det <= read_file_contents(MaybeIO).
|
|
|
|
expand_file_specials_check(ShortOptionPred, LongOptionPred, OptionTable,
|
|
OVs, SeenFileNames, MaybeError, NFSOVs, !MaybeIO) :-
|
|
expand_file_specials_loop(ShortOptionPred, LongOptionPred, OptionTable,
|
|
OVs, SeenFileNames, MaybeError, go_empty_cord, NFSOVsCord, !MaybeIO),
|
|
NFSOVs = go_cord_to_list(NFSOVsCord).
|
|
|
|
:- pred expand_file_specials_loop(short_option(OptionType)::in(short_option),
|
|
long_option(OptionType)::in(long_option),
|
|
option_table(OptionType)::in, list(option_value(OptionType))::in,
|
|
set(string)::in, maybe_option_error(OptionType)::out,
|
|
go_cord(option_value(OptionType))::in(go_cord_skel(non_file_special)),
|
|
go_cord(option_value(OptionType))::out(go_cord_skel(non_file_special)),
|
|
MaybeIO::di, MaybeIO::uo) is det <= read_file_contents(MaybeIO).
|
|
|
|
expand_file_specials_loop(_, _, _, [], _SeenFileNames,
|
|
no_option_error, !NFSOVsCord, !MaybeIO).
|
|
expand_file_specials_loop(ShortOptionPred, LongOptionPred, OptionTable,
|
|
[OV0 | OVs0], SeenFileNames0, MaybeError, !NFSOVsCord, !MaybeIO) :-
|
|
(
|
|
( OV0 = ov_bool(_, _, _)
|
|
; OV0 = ov_int(_, _, _)
|
|
; OV0 = ov_string(_, _, _)
|
|
; OV0 = ov_maybe_int(_, _, _)
|
|
; OV0 = ov_maybe_string(_, _, _)
|
|
; OV0 = ov_accumulating(_, _, _)
|
|
; OV0 = ov_accumulating_reset(_, _)
|
|
; OV0 = ov_special(_, _)
|
|
; OV0 = ov_bool_special(_, _, _)
|
|
; OV0 = ov_int_special(_, _, _)
|
|
; OV0 = ov_string_special(_, _, _)
|
|
; OV0 = ov_maybe_string_special(_, _, _)
|
|
),
|
|
cord_snoc(OV0, !NFSOVsCord),
|
|
expand_file_specials_loop(ShortOptionPred, LongOptionPred, OptionTable,
|
|
OVs0, SeenFileNames0, MaybeError, !NFSOVsCord, !MaybeIO)
|
|
;
|
|
OV0 = ov_file_special(Flag, OptName, FileName),
|
|
( if set.insert_new(FileName, SeenFileNames0, SeenFileNames) then
|
|
expand_file_special_option(ShortOptionPred, LongOptionPred,
|
|
OptionTable, SeenFileNames, Flag, OptName, FileName,
|
|
FileOptionsOrError, !MaybeIO),
|
|
(
|
|
FileOptionsOrError = file_options_no_error(NFSFileOVsCord),
|
|
go_cord_append(!.NFSOVsCord, NFSFileOVsCord, !:NFSOVsCord),
|
|
expand_file_specials_loop(ShortOptionPred, LongOptionPred,
|
|
OptionTable, OVs0, SeenFileNames0, MaybeError,
|
|
!NFSOVsCord, !MaybeIO)
|
|
;
|
|
FileOptionsOrError = file_options_error(Error),
|
|
MaybeError = found_option_error(Error)
|
|
)
|
|
else
|
|
Reason = file_special_circular_inclusion(FileName),
|
|
Error = option_error(Flag, OptName, Reason),
|
|
MaybeError = found_option_error(Error)
|
|
)
|
|
).
|
|
|
|
:- type file_options_or_error(OptionType)
|
|
---> file_options_no_error(go_cord(option_value(OptionType)))
|
|
; file_options_error(option_error(OptionType)).
|
|
|
|
:- inst file_options_or_error for file_options_or_error/1
|
|
---> file_options_no_error(go_cord_skel(non_file_special))
|
|
; file_options_error(ground).
|
|
|
|
:- pred expand_file_special_option(short_option(OptionType)::in(short_option),
|
|
long_option(OptionType)::in(long_option), option_table(OptionType)::in,
|
|
set(string)::in, OptionType::in, string::in, string::in,
|
|
file_options_or_error(OptionType)::out(file_options_or_error),
|
|
MaybeIO::di, MaybeIO::uo) is det <= read_file_contents(MaybeIO).
|
|
|
|
expand_file_special_option(ShortOptionPred, LongOptionPred, OptionTable,
|
|
SeenFileNames, Flag, OptName, FileName, FileOptionsOrError,
|
|
!MaybeIO) :-
|
|
read_file_contents(FileName, ReadFromFileResult, !MaybeIO),
|
|
(
|
|
ReadFromFileResult = read_success(Contents),
|
|
Words = string.words(Contents),
|
|
recognize_options(ShortOptionPred, LongOptionPred, OptionTable,
|
|
Words, FileMaybeError, FileOVs, _OptionArgs, NonOptionArgs),
|
|
(
|
|
FileMaybeError = no_option_error,
|
|
(
|
|
NonOptionArgs = [],
|
|
expand_file_specials_loop(ShortOptionPred, LongOptionPred,
|
|
OptionTable, FileOVs, SeenFileNames, MaybeError,
|
|
go_empty_cord, NFSOVsCord, !MaybeIO),
|
|
(
|
|
MaybeError = no_option_error,
|
|
FileOptionsOrError = file_options_no_error(NFSOVsCord)
|
|
;
|
|
MaybeError = found_option_error(Error),
|
|
FileOptionsOrError = file_options_error(Error)
|
|
)
|
|
;
|
|
NonOptionArgs = [_ | _],
|
|
Reason = file_special_contains_non_option_args(FileName),
|
|
Error = option_error(Flag, OptName, Reason),
|
|
FileOptionsOrError = file_options_error(Error)
|
|
)
|
|
;
|
|
FileMaybeError = found_option_error(FileError),
|
|
FileOptionsOrError = file_options_error(FileError)
|
|
)
|
|
;
|
|
(
|
|
ReadFromFileResult = read_failure_no_io,
|
|
Reason = file_special_but_no_io(FileName)
|
|
;
|
|
ReadFromFileResult = read_failure_open(IO_Error),
|
|
Reason = file_special_cannot_open(FileName, IO_Error)
|
|
;
|
|
ReadFromFileResult = read_failure_read(IO_Error),
|
|
Reason = file_special_cannot_read(FileName, IO_Error)
|
|
),
|
|
Error = option_error(Flag, OptName, Reason),
|
|
FileOptionsOrError = file_options_error(Error)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred process_option_values(
|
|
option_ops_special(OptionType, UserDataType)::in(option_ops_special),
|
|
list(option_value(OptionType))::in(list_skel(non_file_special)),
|
|
maybe_option_error(OptionType)::out,
|
|
option_table(OptionType)::in, option_table(OptionType)::out,
|
|
set_tree234(OptionType)::in, set_tree234(OptionType)::out,
|
|
UserDataType::in, UserDataType::out) is det.
|
|
|
|
process_option_values(_SpecialHandler, [], no_option_error,
|
|
!OptionTable, !OptionsSet, !UserData).
|
|
process_option_values(SpecialHandler, [OV | OVs], MaybeError,
|
|
!OptionTable, !OptionsSet, !UserData) :-
|
|
(
|
|
(
|
|
OV = ov_bool(Flag, _OptName, BoolValue),
|
|
map.set(Flag, bool(BoolValue), !OptionTable)
|
|
;
|
|
OV = ov_int(Flag, _OptName, IntValue),
|
|
map.set(Flag, int(IntValue), !OptionTable)
|
|
;
|
|
OV = ov_maybe_int(Flag, _OptName, MaybeIntValue),
|
|
map.set(Flag, maybe_int(MaybeIntValue), !OptionTable)
|
|
;
|
|
OV = ov_string(Flag, _OptName, StringValue),
|
|
map.set(Flag, string(StringValue), !OptionTable)
|
|
;
|
|
OV = ov_maybe_string(Flag, _OptName, MaybeStringValue),
|
|
map.set(Flag, maybe_string(MaybeStringValue), !OptionTable)
|
|
;
|
|
OV = ov_accumulating(Flag, _OptName, StringValue),
|
|
map.lookup(!.OptionTable, Flag, Value0),
|
|
( if Value0 = accumulating(AccStrings0) then
|
|
AccStrings = AccStrings0 ++ [StringValue],
|
|
map.set(Flag, accumulating(AccStrings), !OptionTable)
|
|
else
|
|
% OV can be ov_accumulating only if the entry for Flag
|
|
% in the option_table is accumulating.
|
|
error($pred, "ov_accumulating but not accumulating")
|
|
)
|
|
;
|
|
OV = ov_accumulating_reset(Flag, _OptName),
|
|
map.set(Flag, accumulating([]), !OptionTable)
|
|
),
|
|
set_tree234.insert(Flag, !OptionsSet),
|
|
process_option_values(SpecialHandler, OVs, MaybeError,
|
|
!OptionTable, !OptionsSet, !UserData)
|
|
;
|
|
(
|
|
OV = ov_special(Flag, OptName),
|
|
OptionData = none
|
|
;
|
|
OV = ov_bool_special(Flag, OptName, BoolValue),
|
|
OptionData = bool(BoolValue)
|
|
;
|
|
OV = ov_int_special(Flag, OptName, IntValue),
|
|
OptionData = int(IntValue)
|
|
;
|
|
OV = ov_string_special(Flag, OptName, StringValue),
|
|
OptionData = string(StringValue)
|
|
;
|
|
OV = ov_maybe_string_special(Flag, OptName, MaybeStringValue),
|
|
OptionData = maybe_string(MaybeStringValue)
|
|
),
|
|
set_tree234.insert(Flag, !OptionsSet),
|
|
process_special_option(SpecialHandler, Flag, OptName, OptionData,
|
|
MaybeSpecialError, !OptionTable, !OptionsSet, !UserData),
|
|
(
|
|
MaybeSpecialError = no_option_error,
|
|
process_option_values(SpecialHandler, OVs, MaybeError,
|
|
!OptionTable, !OptionsSet, !UserData)
|
|
;
|
|
MaybeSpecialError = found_option_error(_),
|
|
MaybeError = MaybeSpecialError
|
|
)
|
|
).
|
|
|
|
:- pred process_special_option(
|
|
option_ops_special(OptionType, UserDataType)::in(option_ops_special),
|
|
OptionType::in, string::in, special_data::in,
|
|
maybe_option_error(OptionType)::out,
|
|
option_table(OptionType)::in, option_table(OptionType)::out,
|
|
set_tree234(OptionType)::in, set_tree234(OptionType)::out,
|
|
UserDataType::in, UserDataType::out) is det.
|
|
|
|
process_special_option(SpecialHandler, Flag, OptName, OptionData,
|
|
MaybeError, !OptionTable, !OptionsSet, !UserData) :-
|
|
(
|
|
SpecialHandler = none,
|
|
Error = option_error(Flag, OptName, special_handler_missing),
|
|
MaybeError = found_option_error(Error)
|
|
;
|
|
SpecialHandler = notrack(Handler),
|
|
( if
|
|
Handler(Flag, OptionData, !.OptionTable, Result0)
|
|
then
|
|
(
|
|
Result0 = ok(NewOptionTable),
|
|
!:OptionTable = NewOptionTable,
|
|
MaybeError = no_option_error
|
|
;
|
|
Result0 = error(HandlerMsg),
|
|
Reason = special_handler_error(HandlerMsg),
|
|
Error = option_error(Flag, OptName, Reason),
|
|
MaybeError = found_option_error(Error)
|
|
% Leave !OptionTable as it was before the error.
|
|
)
|
|
else
|
|
Error = option_error(Flag, OptName, special_handler_failed),
|
|
MaybeError = found_option_error(Error)
|
|
)
|
|
;
|
|
SpecialHandler = track(TrackHandler),
|
|
( if
|
|
TrackHandler(Flag, OptionData, !.OptionTable, Result0,
|
|
NewOptionsSet)
|
|
then
|
|
NewOptions = set.to_sorted_list(NewOptionsSet),
|
|
set_tree234.insert_list(NewOptions, !OptionsSet),
|
|
(
|
|
Result0 = ok(NewOptionTable),
|
|
!:OptionTable = NewOptionTable,
|
|
MaybeError = no_option_error
|
|
;
|
|
Result0 = error(TrackHandlerMsg),
|
|
Reason = special_handler_error(TrackHandlerMsg),
|
|
Error = option_error(Flag, OptName, Reason),
|
|
MaybeError = found_option_error(Error)
|
|
% Leave !OptionTable as it was before the error.
|
|
)
|
|
else
|
|
Error = option_error(Flag, OptName, special_handler_failed),
|
|
MaybeError = found_option_error(Error)
|
|
)
|
|
;
|
|
SpecialHandler = userdata(UserDataHandler),
|
|
( if
|
|
UserDataHandler(Flag, OptionData, !.OptionTable, Result0,
|
|
!.UserData, NewUserData)
|
|
then
|
|
(
|
|
Result0 = ok(NewOptionTable),
|
|
MaybeError = no_option_error,
|
|
!:OptionTable = NewOptionTable,
|
|
!:UserData = NewUserData
|
|
;
|
|
Result0 = error(UserDataHandlerMsg),
|
|
Reason = special_handler_error(UserDataHandlerMsg),
|
|
Error = option_error(Flag, OptName, Reason),
|
|
MaybeError = found_option_error(Error)
|
|
% Leave !OptionTable/!UserData as they were before the error.
|
|
)
|
|
else
|
|
Error = option_error(Flag, OptName, special_handler_failed),
|
|
MaybeError = found_option_error(Error)
|
|
)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred numeric_argument_error(OptionType::in, string::in, string::in,
|
|
option_error(OptionType)::out) is det.
|
|
|
|
numeric_argument_error(Flag, OptName, Arg, Error) :-
|
|
Reason = requires_numeric_argument(Arg),
|
|
Error = option_error(Flag, OptName, Reason).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
lookup_bool_option(OptionTable, Opt) = Bool :-
|
|
lookup_bool_option(OptionTable, Opt, Bool).
|
|
|
|
lookup_bool_option(OptionTable, Opt, Bool) :-
|
|
map.lookup(OptionTable, Opt, Value),
|
|
( if Value = bool(BoolPrime) then
|
|
Bool = BoolPrime
|
|
else
|
|
OptStr = string.string(Opt),
|
|
unexpected($pred, OptStr ++ " is not a bool option")
|
|
).
|
|
|
|
lookup_int_option(OptionTable, Opt) = Int :-
|
|
lookup_int_option(OptionTable, Opt, Int).
|
|
|
|
lookup_int_option(OptionTable, Opt, Int) :-
|
|
map.lookup(OptionTable, Opt, Value),
|
|
( if Value = int(IntPrime) then
|
|
Int = IntPrime
|
|
else
|
|
OptStr = string.string(Opt),
|
|
unexpected($pred, OptStr ++ " is not an int option")
|
|
).
|
|
|
|
lookup_string_option(OptionTable, Opt) = Str :-
|
|
lookup_string_option(OptionTable, Opt, Str).
|
|
|
|
lookup_string_option(OptionTable, Opt, Str) :-
|
|
map.lookup(OptionTable, Opt, Value),
|
|
( if Value = string(StrPrime) then
|
|
Str = StrPrime
|
|
else
|
|
OptStr = string.string(Opt),
|
|
unexpected($pred, OptStr ++ " is not a string option")
|
|
).
|
|
|
|
lookup_maybe_int_option(OptionTable, Opt) = MaybeInt :-
|
|
lookup_maybe_int_option(OptionTable, Opt, MaybeInt).
|
|
|
|
lookup_maybe_int_option(OptionTable, Opt, MaybeInt) :-
|
|
map.lookup(OptionTable, Opt, Value),
|
|
( if Value = maybe_int(MaybeIntPrime) then
|
|
MaybeInt = MaybeIntPrime
|
|
else
|
|
OptStr = string.string(Opt),
|
|
unexpected($pred, OptStr ++ " is not a maybe_int option")
|
|
).
|
|
|
|
lookup_maybe_string_option(OptionTable, Opt) = MaybeString :-
|
|
lookup_maybe_string_option(OptionTable, Opt, MaybeString).
|
|
|
|
lookup_maybe_string_option(OptionTable, Opt, MaybeString) :-
|
|
map.lookup(OptionTable, Opt, Value),
|
|
( if Value = maybe_string(MaybeStringPrime) then
|
|
MaybeString = MaybeStringPrime
|
|
else
|
|
OptStr = string.string(Opt),
|
|
unexpected($pred, OptStr ++ " is not a maybe_string option")
|
|
).
|
|
|
|
lookup_accumulating_option(OptionTable, Opt) = Acc :-
|
|
lookup_accumulating_option(OptionTable, Opt, Acc).
|
|
|
|
lookup_accumulating_option(OptionTable, Opt, Acc) :-
|
|
map.lookup(OptionTable, Opt, Value),
|
|
( if Value = accumulating(AccPrime) then
|
|
Acc = AccPrime
|
|
else
|
|
OptStr = string.string(Opt),
|
|
unexpected($pred, OptStr ++ " is not an accumulating option")
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
option_error_to_string(Error) = String :-
|
|
(
|
|
Error = unrecognized_option(OptionName),
|
|
string.format("unrecognized option `%s'", [s(OptionName)], String)
|
|
;
|
|
Error = option_error(_, OptionName, Reason),
|
|
(
|
|
Reason = unknown_type,
|
|
string.format("unknown type for option `%s'",
|
|
[s(OptionName)], String)
|
|
;
|
|
Reason = requires_argument,
|
|
string.format("option `%s' needs an argument",
|
|
[s(OptionName)], String)
|
|
;
|
|
Reason = does_not_allow_argument(_),
|
|
string.format("option `%s' does not allow an argument",
|
|
[s(OptionName)], String)
|
|
;
|
|
Reason = cannot_negate,
|
|
string.format("cannot negate option `%s' -- " ++
|
|
"only boolean, maybe and accumulating options can be negated",
|
|
[s(OptionName)], String)
|
|
;
|
|
Reason = special_handler_failed,
|
|
string.format("the handler of option `%s' failed",
|
|
[s(OptionName)], String)
|
|
;
|
|
Reason = special_handler_missing,
|
|
string.format("option `%s' has no handler",
|
|
[s(OptionName)], String)
|
|
;
|
|
Reason = special_handler_error(String)
|
|
;
|
|
Reason = requires_numeric_argument(Arg),
|
|
string.format(
|
|
"option `%s' requires a numeric argument; `%s' is not numeric",
|
|
[s(OptionName), s(Arg)], String)
|
|
;
|
|
Reason = file_special_but_no_io(FileName),
|
|
Msg = "the option processing predicate has no access to I/O",
|
|
string.format("cannot open %s: %s", [s(FileName), s(Msg)], String)
|
|
;
|
|
Reason = file_special_cannot_open(FileName, IO_Error),
|
|
io.error_message(IO_Error, Msg),
|
|
string.format("cannot open %s: %s", [s(FileName), s(Msg)], String)
|
|
;
|
|
Reason = file_special_cannot_read(FileName, IO_Error),
|
|
io.error_message(IO_Error, Msg),
|
|
string.format("cannot read %s: %s", [s(FileName), s(Msg)], String)
|
|
;
|
|
Reason = file_special_contains_non_option_args(FileName),
|
|
string.format("%s contains non-option arguments",
|
|
[s(FileName)], String)
|
|
;
|
|
Reason = file_special_circular_inclusion(FileName),
|
|
string.format("%s includes itself either directly or indirectly",
|
|
[s(FileName)], String)
|
|
)
|
|
).
|
|
|
|
convert_to_maybe_option_table(MaybeStructuredError) = MaybeError :-
|
|
(
|
|
MaybeStructuredError = ok(OptionTable),
|
|
MaybeError = ok(OptionTable)
|
|
;
|
|
MaybeStructuredError = error(Error),
|
|
MaybeError = error(option_error_to_string(Error))
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% The rest of this module contains a version of the cord type that
|
|
% supports operations that preserve the insts of the items in the cord.
|
|
% We duplicate only the subset of the cord operations that we need.
|
|
%
|
|
|
|
:- type go_cord(T)
|
|
---> go_empty_cord
|
|
; go_nonempty_cord(go_cord_node(T)).
|
|
|
|
:- inst go_cord_skel(I) for go_cord/1
|
|
---> go_empty_cord
|
|
; go_nonempty_cord(go_cord_node_skel(I)).
|
|
|
|
:- type go_cord_node(T)
|
|
---> go_unit_node(T)
|
|
; go_list_node(T, list(T))
|
|
; go_branch_node(go_cord_node(T), go_cord_node(T)).
|
|
|
|
:- inst go_cord_node_skel(I) for go_cord_node/1
|
|
---> go_unit_node(I)
|
|
; go_list_node(I, list_skel(I))
|
|
; go_branch_node(go_cord_node_skel(I), go_cord_node_skel(I)).
|
|
|
|
:- func go_cord_to_list(go_cord(T)::in(go_cord_skel(I =< ground)))
|
|
= (list(T)::out(list_skel(I =< ground))) is det.
|
|
|
|
go_cord_to_list(go_empty_cord) = [].
|
|
go_cord_to_list(go_nonempty_cord(N)) = go_cord_to_list_2([N], []).
|
|
|
|
:- func go_cord_to_list_2(
|
|
list(go_cord_node(T))::in(list_skel(go_cord_node_skel(I =< ground))),
|
|
list(T)::in(list_skel(I =< ground)))
|
|
= (list(T)::out(list_skel(I =< ground))) is det.
|
|
|
|
go_cord_to_list_2([], L) = L.
|
|
go_cord_to_list_2([N | Ns], L0) = L :-
|
|
(
|
|
N = go_unit_node(X),
|
|
L = go_cord_to_list_2(Ns, [X | L0])
|
|
;
|
|
N = go_list_node(H, T),
|
|
TL0 = list.inst_preserving_append(T, L0),
|
|
L = go_cord_to_list_2(Ns, [H | TL0])
|
|
;
|
|
N = go_branch_node(A, B),
|
|
L = go_cord_to_list_2([B, A | Ns], L0)
|
|
).
|
|
|
|
:- pred go_cord_append(
|
|
go_cord(T)::in(go_cord_skel(I =< ground)),
|
|
go_cord(T)::in(go_cord_skel(I =< ground)),
|
|
go_cord(T)::out(go_cord_skel(I =< ground))) is det.
|
|
|
|
go_cord_append(A, B, C) :-
|
|
(
|
|
A = go_empty_cord,
|
|
C = B
|
|
;
|
|
A = go_nonempty_cord(_),
|
|
B = go_empty_cord,
|
|
C = A
|
|
;
|
|
A = go_nonempty_cord(AN),
|
|
B = go_nonempty_cord(BN),
|
|
C = go_nonempty_cord(go_branch_node(AN, BN))
|
|
).
|
|
|
|
:- pred cord_snoc(T::in(I =< ground),
|
|
go_cord(T)::in(go_cord_skel(I =< ground)),
|
|
go_cord(T)::out(go_cord_skel(I =< ground))) is det.
|
|
|
|
cord_snoc(X, C, CX) :-
|
|
(
|
|
C = go_empty_cord,
|
|
CX = go_nonempty_cord(go_unit_node(X))
|
|
;
|
|
C = go_nonempty_cord(N),
|
|
CX = go_nonempty_cord(go_branch_node(N, go_unit_node(X)))
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
:- end_module getopt.
|
|
%---------------------------------------------------------------------------%
|