[m-rev.] for review: unify getopt and getopt_io
Peter Wang
novalazy at gmail.com
Fri Oct 9 15:08:13 AEDT 2020
On Fri, 09 Oct 2020 06:07:04 +1100 "Zoltan Somogyi" <zoltan.somogyi at runbox.com> wrote:
> For review by anyone, though I would like both Peter and Julien
> to have a look at the concept, and at the additional help for
> diagnosing breaking changes.
>
> I intend to post a diff for NEWS tomorrow, covering both this change,
> and other recent changes to getopt*.m.
>
> Zoltan.
> diff --git a/compiler/typecheck_errors.m b/compiler/typecheck_errors.m
> index 527c205e6..cc61da53c 100644
> --- a/compiler/typecheck_errors.m
> +++ b/compiler/typecheck_errors.m
> +:- pred report_special_type_mismatches_loop(is_first::in,
> + list(type_mismatch_special)::in, list(format_component)::out) is det.
> +
> +report_special_type_mismatches_loop(_IsFirst, [], []).
> +report_special_type_mismatches_loop(IsFirst, [HeadSpecial | TailSpecials],
> + Pieces) :-
Indent.
> + report_special_type_mismatches_loop(is_not_first, TailSpecials,
> + TailPieces),
> + HeadPieces = report_special_type_mismatch(IsFirst, HeadSpecial),
> + Pieces = HeadPieces ++ TailPieces.
> +
> +:- func report_special_type_mismatch(is_first, type_mismatch_special)
> + = list(format_component).
> +
> +report_special_type_mismatch(IsFirst, MismatchSpecial) = Pieces :-
> + (
> + IsFirst = is_first,
> + ReasonIsPieces =
> + [words("One possible reason for the error is that")]
> + ;
> + IsFirst = is_not_first,
> + ReasonIsPieces =
> + [words("Another possible reason for the error is that")]
> + ),
> + (
> + MismatchSpecial = type_mismatch_special_getopt_error(GetoptModule),
> + Pieces = ReasonIsPieces ++
> + [words("the signatures of the option processing predicates"),
> + words("in the"), quote(GetoptModule), words("module"),
> + words("have changed recently."),
> + words("Errors are now returned in a structured form,"),
> + words("which can be converted to a string by calling the"),
> + quote("option_error_to_string"), words("function."), nl]
> + ).
Don't need the parentheses.
> diff --git a/library/getopt_io.m b/library/getopt_io.m
> index e6361d1e2..a0afaf32e 100644
> --- a/library/getopt_io.m
> +++ b/library/getopt_io.m
> @@ -105,28 +71,24 @@
> % "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.
> +%
I suggest to put "other than file_special" in parentheses to make the
sentence easier to parse.
> +% 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 as argument does nevertheless specify
Fix "argument", or delete "as argument".
> +% a file_special option, that predicate will generate an error message
> +% for that option.
Perhaps simply:
then an error will be reported.
> @@ -428,69 +378,107 @@
> % The option is a file_special option whose argument is the file
> % named by the argument. This file contained some non-option
> % arguments.
> -% GETOPT_IO_ONLY_END
>
> %---------------------------------------------------------------------------%
>
> - % process_options(OptionOps, Args, NonOptionArgs, Result, !IO):
> - % process_options(OptionOps, Args, OptionArgs, NonOptionArgs, Result, !IO):
> + % process_options(OptionOps, Args, NonOptionArgs, Result):
> + % process_options(OptionOps, Args, OptionArgs, NonOptionArgs, Result):
> + % process_option_io(OptionOps, Args, NonOptionArgs,
> + % Result, !IO):
> + % process_option_io(OptionOps, Args, OptionArgs, NonOptionArgs,
> + % Result, !IO):
Missing "s" on the two _io predicates.
> + %
> + % 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
> + % defaul values, and possibly the handler for the special options.
default
> + % The structure of the `OptionOps' argument is documented above,
> + % at the definition of the option_ops type.
> %
> - % 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.
> + % 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.)
I think we should deprecate
process_options(OptionOps, Args, NonOptionArgs, Result)
and don't introduce
process_options_io(OptionOps, Args, NonOptionArgs, Result, !IO)
> %
> - % If an invalid option is encountered, we return `error(Message)',
> - % otherwise we return `ok(OptionTable)' in 'Result'.
> + % 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.
> %
> - % The structure of the `OptionOps' argument is documented above.
> + % In they do not find a problem, all these predicates will place into
If they
> + % `Result' an `ok' wrapped around an option table, which maps each option
> + % to its final value. This will be its default value as updated by
> + % all the options in `Args'.
> %
The mention of "default values" being "updated" was confusing to me
(of course the intended meaning can be grasped eventually).
Maybe replace the last part with something like:
which maps each option to a value, as set by the options in `Args'.
> - % The two different arity versions differ only in that the lower arity
> - % version does not return the arguments that contained the options.
> - % While some callers will want those arguments, other callers will not,
> - % considering that the only information they want from them is that
> - % contained in the option table.
> + % 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 its contents as specifying
treating the file's contents
> + % additional options. The predicate versions whose names do not end in `io'
> + % cannot do I/O, and therefore cannot handle file_special options.
> + % If they encounter them anyway, they will report that fact as an error.
I suggest:
cannot do I/O, and will report an error if they encounter a
file_special option.
> % process_options_track(OptionOps, Args, OptionArgs, NonOptionArgs,
> + % OptionTable0, Result, OptionsSet):
> + % process_options_track_io(OptionOps, Args, OptionArgs, NonOptionArgs,
> % OptionTable0, Result, OptionsSet, !IO):
> %
> - % This predicate differs from the two variants of process_options
> - % above in only two respects.
> + % These predicates differ from the non-track variants above
> + % in only two respects.
> %
> - % First, it expects the caller to supply an argument containing
> + % First, they expects the caller to supply an argument containing
expect
> % the initial contents of the option table, instead of calling
> - % the initialization predicate itself. The point of this is that
> + % the initialization predicate themselves. The point of this is that
> % it allows the option table to be initialized once (using either
I suggest replacing:
The point of this is that it allows ...
with:
It allows the option table ...
> - % init_option_table or init_option_table_multi below), and then
> - % process_options_track to be called several times with different
> - % sets of arguments, perhaps obtained from different sources
> - % (command line, configuration file, etc).
> + % the init_option_table or the init_option_table_multi predicate below),
> + % and then process_options_track or process_options_track_io
> + % can be called 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 process_options_track returns the set of options
> - % that were set by that call. This helps with the same objective;
> - % for example, it can tell the caller whether an option was set
> - % from a configuration file, the command line, both, or neither.
> + % Second, each call to these predicates returns the set of options
return
> % process_options_userdata(OptionOps, Args, OptionArgs, NonOptionArgs,
> + % MaybeError, OptionsSet, !OptionTable, !UserData):
> + % process_options_userdata_io(OptionOps, Args, OptionArgs, NonOptionArgs,
> % MaybeError, OptionsSet, !OptionTable, !UserData, !IO):
> %
> - % This predicate is similar to process_options_track, but differs
> + % These predicates are similar to the track predicates above, but differ
> % in two ways.
> %
> - % - It also threads a piece of state of a user-specified "userdata" type
> + % - They also threads a piece of state of a user-specified "userdata" type
thread
> % 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
> @@ -498,33 +486,17 @@
> % information that is not present in the (orderless) set
> % of specified options.
> %
> - % - Even if it finds an error, it returns the option table as it was
> + % - Even if they find an error, they returns the option table as it was
return
> @@ -580,8 +552,18 @@
>
> %---------------------------------------------------------------------------%
>
> + % Convert our structured representation of an error to an error message.
> + %
> :- func option_error_to_string(option_error(OptionType)) = string.
s/our/from the/
> +%---------------------%
> +
> + % If the argument represents an error, then that error from
> + % our structured representation to an error message.
> + %
convert that error from the structured representation to
> +:- func decode_maybe_option_table(maybe_option_table_se(OptionType))
> + = maybe_option_table(OptionType).
> +
"decode" seems a strange name for this function, but I can only suggest
"convert_to_maybe_option_table".
The rest looks fine to me.
Peter
More information about the reviews
mailing list