[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: argmatch: accept perfect matches in documented arglists
From: |
Akim Demaille |
Subject: |
Re: argmatch: accept perfect matches in documented arglists |
Date: |
Tue, 21 May 2019 09:16:07 +0200 |
Hi Paul, hi Bruno,
> Le 19 mai 2019 à 18:13, Bruno Haible <address@hidden> a écrit :
>
> Hi Akim,
>
>> What do you think of something like this?
>
> Looks very nice.
>
> Now, can the aforementioned issue with the existing argmatch() interface
> be solved in the same way?
I'm making my way into it, slowly.
Here is my new state of affairs. I still use static inline, because with
static only, GCC complains about unused functions. If you have suggestions on
the gnulib recommended way of dealing with this, I'll adjust the code.
One change I made is to compute the documentation column instead of always
using 20. In the case of Bison that gives:
> $ LC_ALL=en bison --help
> ...
> Warning categories include:
> conflicts-sr S/R conflicts (enabled by default)
> conflicts-rr R/R conflicts (enabled by default)
> deprecated obsolete constructs
> empty-rule empty rules without %empty
> midrule-values unset or unused midrule values
> precedence useless precedence and associativity
> yacc incompatibilities with POSIX Yacc
> other all other warnings (enabled by default)
> all all the warnings except 'yacc'
> no-CATEGORY turn off warnings in CATEGORY
> none turn off all the warnings
> error[=CATEGORY] treat warnings as errors
>
> THINGS is a list of comma separated words that can include:
> states describe the states
> itemsets complete the core item sets with their closure
> lookaheads, look-ahead
> explicitly associate lookahead tokens to items
> solved describe shift/reduce conflicts solving
> all include all the above information
> none disable the report
>
> FEATURES is a list of comma separated words that can include:
> caret, diagnostics-show-caret
> show errors with carets
> fixit, diagnostics-parseable-fixits
> show machine-readable fixes
> syntax-only do not generate any file
> all all of the above
> none disable all of the above
>
> TRACES is a list of comma separated words that can include:
> none no traces
> locations full display of the locations
> scan grammar scanner traces
> parse grammar parser traces
> automaton construction of the automaton
> bitsets use of bitsets
> closure input/output of closure
> grammar reading, reducing the grammar
> resource memory consumption (where available)
> sets grammar sets: firsts, nullable etc.
> muscles m4 definitions passed to the skeleton
> tools m4 invocation
> m4 m4 traces
> skeleton skeleton postprocessing
> time time consumption
> ielr IELR conversion
> all all of the above
Another change I made is on the documentation type. It used to map values to
doc, I decided to map arguments (i.e., the user-exposed string) to doc instead,
for two reasons. Reason 1, it fits nicely with the case of Bison where we also
document pseudo values:
> $ LC_ALL=en bison --help
> ...
> Warning categories include:
> 'conflicts-sr' S/R conflicts (enabled by default)
> 'conflicts-rr' R/R conflicts (enabled by default)
> 'deprecated' obsolete constructs
> 'empty-rule' empty rules without %empty
> 'midrule-values' unset or unused midrule values
> 'precedence' useless precedence and associativity
> 'yacc' incompatibilities with POSIX Yacc
> 'other' all other warnings (enabled by default)
> 'all' all the warnings except 'yacc'
> 'no-CATEGORY' turn off warnings in CATEGORY
> 'none' turn off all the warnings
> 'error[=CATEGORY]' treat warnings as errors
The line no-CATEGORY and error[=CATEGORY] are not real values, obviously, yet I
want to be able to document them like any other. Note that the penultimate
line (none) _is_ a real value, so I cannot achieve the same result with the
post-documentation (in addition, I couldn't do that comfortably now that the
documentation column is flexible).
Reason 2 is that sometimes the values are "complex":
> static const argmatch_report_arg argmatch_report_args[] =
> {
> { "none", report_none },
> { "states", report_states },
> { "itemsets", report_states | report_itemsets },
> { "lookaheads", report_states | report_lookahead_tokens },
> { "look-ahead", report_states | report_lookahead_tokens },
> { "solved", report_states | report_solved_conflicts },
> { "all", report_all },
> { NULL, report_none },
> };
In that case it is much easier, much simpler and more natural to write
> { "itemsets", N_("complete the core item sets with their closure") },
> { "lookaheads", N_("explicitly associate lookahead tokens to items") },
> { "solved", N_("describe shift/reduce conflicts solving") },
instead of
> { report_states | report_itemsets, N_("complete the core item sets
> with their closure") },
> { report_states | report_lookahead_tokens, N_("explicitly associate
> lookahead tokens to items") },
> { report_states | report_solved_conflicts, N_("describe shift/reduce
> conflicts solving") },
I have focused first on what Bison needs from argmatch, so it's not yet feature
-complete with the previous API, but it's a significant part of it.
I have not changed the behavior on errors:
> $ bison --report=foobar
> bison: invalid argument 'foobar' for '--report'
> Valid arguments are:
> - 'none'
> - 'states'
> - 'itemsets'
> - 'lookaheads', 'look-ahead'
> - 'solved'
> - 'all'
But:
- 1. Do we keep the quotes here? Bison used to have them when
documenting the argmatch values,
Bison 3.4:
> THINGS is a list of comma separated words that can include:
> 'state' describe the states
> 'itemset' complete the core item sets with their closure
> 'lookahead' explicitly associate lookahead tokens to items
but the Coreutils don't when they document backup options:
> The backup suffix is '~', unless set with --suffix or SIMPLE_BACKUP_SUFFIX.
> The version control method may be selected via the --backup option or through
> the VERSION_CONTROL environment variable. Here are the values:
>
> none, off never make backups (even if --backup is given)
> numbered, t make numbered backups
> existing, nil numbered if numbered backups exist, simple otherwise
> simple, never always make simple backups
- 2. I'd like to point to the documentation, but not force the
documentation here. What we could do is support implicitly the
"help" argument when it's not already existing, and display this
help. In the case of Bison, that would mean
bison --report=help
Below are two (draft) commits: the proposal for gnulib, and the corresponding
patch for Bison that shows a real use case for these changes.
==============================================================
commit 01e0cafc9a22160ffca29aeb99ed004b090ccf23
Author: Akim Demaille <address@hidden>
Date: Tue Apr 30 08:01:14 2019 +0200
WIP: argmatch: provide support for documentation
diff --git a/lib/argmatch.c b/lib/argmatch.c
index 16597c4b6..065fa398b 100644
--- a/lib/argmatch.c
+++ b/lib/argmatch.c
@@ -29,12 +29,8 @@
#include <stdlib.h>
#include <string.h>
-#include "gettext.h"
-#define _(msgid) gettext (msgid)
-
#include "error.h"
#include "quotearg.h"
-#include "quote.h"
#include "getprogname.h"
#if USE_UNLOCKED_IO
diff --git a/lib/argmatch.h b/lib/argmatch.h
index 50de57f29..39688c8e4 100644
--- a/lib/argmatch.h
+++ b/lib/argmatch.h
@@ -22,13 +22,21 @@
#ifndef ARGMATCH_H_
# define ARGMATCH_H_ 1
+# include <stdbool.h>
# include <stddef.h>
+# include <stdio.h>
+# include <string.h> /* memcmp */
+# include "gettext.h"
+# include "quote.h"
# include "verify.h"
-#ifdef __cplusplus
+# define _(Msgid) gettext (Msgid)
+# define N_(Msgid) (Msgid)
+
+# ifdef __cplusplus
extern "C" {
-#endif
+# endif
# define ARRAY_CARDINALITY(Array) (sizeof (Array) / sizeof *(Array))
@@ -104,8 +112,191 @@ char const *argmatch_to_argument (void const *value,
argmatch_to_argument (Value, Arglist, \
(void const *) (Vallist), sizeof *(Vallist))
-#ifdef __cplusplus
+# define ARGMATCH_DEFINE_GROUP(Name, Type) \
+ typedef Type argmatch_##Name##_type; \
+ \
+ /* The size of Type. */ \
+ enum argmatch_##Name##_size_enum \
+ { \
+ argmatch_##Name##_size = sizeof (argmatch_##Name##_type) \
+ }; \
+ \
+ /* Documentation. */ \
+ typedef struct \
+ { \
+ /* Argument (e.g., "simple"). */ \
+ const char const *arg; \
+ /* Documentatio (e.g., N_("make numbered backups")). */ \
+ const char const *doc; \
+ } argmatch_##Name##_doc; \
+ \
+ /* Argument mapping. */ \
+ typedef struct \
+ { \
+ /* Argument (e.g., "simple"). */ \
+ const char const *arg; \
+ /* Value (e.g., simple_backups). */ \
+ const argmatch_##Name##_type val; \
+ } argmatch_##Name##_arg; \
+ \
+ /* All the features of an argmatch group. */ \
+ typedef struct \
+ { \
+ /* Printed before the usage message. */ \
+ const char *doc_pre; \
+ /* Printed after the usage message. */ \
+ const char *doc_post; \
+ \
+ const argmatch_##Name##_doc* docs; \
+ const argmatch_##Name##_arg* args; \
+ } argmatch_##Name##_group_type; \
+ \
+ /* The structure the user must build. */ \
+ extern const argmatch_##Name##_group_type argmatch_##Name##_group; \
+ \
+ static inline ptrdiff_t \
+ argmatch_##Name##_match (const char *arg) \
+ { \
+ const argmatch_##Name##_group_type *g = &argmatch_##Name##_group; \
+ size_t size = argmatch_##Name##_size; \
+ ptrdiff_t res = -1; /* Index of first nonexact match. */ \
+ bool ambiguous = false; /* If true, multiple nonexact match(es). */ \
+ size_t arglen = strlen (arg); \
+ \
+ /* Test all elements for either exact match or abbreviated matches. */ \
+ for (size_t i = 0; g->args[i].arg; i++) \
+ if (!strncmp (g->args[i].arg, arg, arglen)) \
+ { \
+ if (strlen (g->args[i].arg) == arglen) \
+ /* Exact match found. */ \
+ return i; \
+ else if (res == -1) \
+ /* First nonexact match found. */ \
+ res = i; \
+ else if (memcmp (&g->args[res].val, &g->args[i].val, \
+ size)) \
+ /* Second nonexact match found. */ \
+ /* There is a real ambiguity, or we could not \
+ disambiguate. */ \
+ ambiguous = true; \
+ } \
+ return ambiguous ? -2 : res; \
+ } \
+ \
+ static void \
+ argmatch_##Name##_valid (FILE *out) \
+ { \
+ const argmatch_##Name##_group_type *g = &argmatch_##Name##_group; \
+ size_t size = argmatch_##Name##_size; \
+ \
+ /* We try to put synonyms on the same line. The assumption is \
+ that synonyms follow each other */ \
+ fputs (_("Valid arguments are:"), out); \
+ for (int i = 0; g->args[i].arg; i++) \
+ if (i == 0 \
+ || memcmp (&g->args[i-1].val, &g->args[i].val, size)) \
+ fprintf (out, "\n - %s", quote (g->args[i].arg)); \
+ else \
+ fprintf (out, ", %s", quote (g->args[i].arg)); \
+ putc ('\n', out); \
+ } \
+ \
+ static inline const argmatch_##Name##_type* \
+ argmatch_##Name##_xmatch (const char *context, const char *arg) \
+ { \
+ const argmatch_##Name##_group_type *g = &argmatch_##Name##_group; \
+ ptrdiff_t res = argmatch_##Name##_match (arg); \
+ if (res < 0) \
+ { \
+ argmatch_invalid (context, arg, res); \
+ argmatch_##Name##_valid (stderr); \
+ argmatch_die (); \
+ } \
+ return &g->args[res].val; \
+ } \
+ \
+ /* The column in which the documentation is displayed. \
+ The leftmost possible, but no more than 20. */ \
+ \
+ static int \
+ argmatch_##Name##_doc_col (void) \
+ { \
+ const argmatch_##Name##_group_type *g = &argmatch_##Name##_group; \
+ size_t size = argmatch_##Name##_size; \
+ int res = 0; \
+ for (int i = 0; g->docs[i].arg; ++i) \
+ { \
+ int col = 4; \
+ int ival = argmatch_##Name##_match (g->docs[i].arg); \
+ if (ival < 0) \
+ /* Pseudo argument, display it. */ \
+ col += strlen (g->docs[i].arg); \
+ else \
+ /* Genuine argument, display it with its synonyms. */ \
+ for (int j = 0; g->args[j].arg; ++j) \
+ if (! memcmp (&g->args[ival].val, &g->args[j].val, size)) \
+ col += (col == 4 ? 0 : 2) + strlen (g->args[j].arg); \
+ if (col <= 20 && res <= col) \
+ res = col; \
+ } \
+ return res ? res : 20; \
+ } \
+ \
+ static void \
+ argmatch_##Name##_usage (FILE *out) \
+ { \
+ const argmatch_##Name##_group_type *g = &argmatch_##Name##_group; \
+ size_t size = argmatch_##Name##_size; \
+ /* Width of the screen. Help2man does not seem to support \
+ arguments on several lines, so in that case pretend a very \
+ large width. */ \
+ const int screen_width = getenv ("HELP2MAN") ? INT_MAX : 80; \
+ if (g->doc_pre) \
+ fprintf (out, "%s\n", _(g->doc_pre)); \
+ int doc_col = argmatch_##Name##_doc_col (); \
+ for (int i = 0; g->docs[i].arg; ++i) \
+ { \
+ int col = 0; \
+ bool first = true; \
+ int ival = argmatch_##Name##_match (g->docs[i].arg); \
+ if (ival < 0) \
+ /* Pseudo argument, display it. */ \
+ col += fprintf (out, " %s", g->docs[i].arg); \
+ else \
+ /* Genuine argument, display it with its synonyms. */ \
+ for (int j = 0; g->args[j].arg; ++j) \
+ if (! memcmp (&g->args[ival].val, &g->args[j].val, size)) \
+ { \
+ if (!first \
+ && screen_width < col + 2 + strlen (g->args[j].arg)) \
+ { \
+ fprintf (out, ",\n"); \
+ col = 0; \
+ first = true; \
+ } \
+ if (first) \
+ { \
+ col += fprintf (out, " "); \
+ first = false; \
+ } \
+ else \
+ col += fprintf (out, ","); \
+ col += fprintf (out, " %s", g->args[j].arg); \
+ } \
+ /* The doc. Separated by at least two spaces. */ \
+ if (doc_col < col + 2) \
+ { \
+ fprintf (out, "\n"); \
+ col = 0; \
+ } \
+ fprintf (out, "%*s%s\n", doc_col - col, "", _(g->docs[i].doc)); \
+ } \
+ if (g->doc_post) \
+ fprintf (out, "%s\n", _(g->doc_post)); \
+ } \
+
+# ifdef __cplusplus
}
-#endif
+# endif
#endif /* ARGMATCH_H_ */
diff --git a/tests/test-argmatch.c b/tests/test-argmatch.c
index 9335adf55..b6e4c99b3 100644
--- a/tests/test-argmatch.c
+++ b/tests/test-argmatch.c
@@ -59,40 +59,92 @@ static const enum backup_type backup_vals[] =
numbered_backups, numbered_backups, numbered_backups
};
+ARGMATCH_DEFINE_GROUP(backup, enum backup_type);
+
+static const argmatch_backup_doc argmatch_backup_docs[] =
+{
+ { "no", N_("never make backups (even if --backup is given)") },
+ { "simple", N_("make numbered backups") },
+ { "existing", N_("numbered if numbered backups exist, simple otherwise") },
+ { "numbered", N_("always make simple backups") },
+ { "foo", N_("unknown feature") },
+ { NULL, NULL }
+};
+
+static const argmatch_backup_arg argmatch_backup_args[] =
+{
+ { "no", no_backups },
+ { "none", no_backups },
+ { "off", no_backups },
+ { "simple", simple_backups },
+ { "never", simple_backups },
+ { "single", simple_backups },
+ { "existing", numbered_existing_backups },
+ { "nil", numbered_existing_backups },
+ { "numbered-existing", numbered_existing_backups },
+ { "numbered", numbered_backups },
+ { "t", numbered_backups },
+ { "newstyle", numbered_backups },
+ { NULL, no_backups }
+};
+
+const argmatch_backup_group_type argmatch_backup_group =
+{
+ N_("\
+The backup suffix is '~', unless set with --suffix or SIMPLE_BACKUP_SUFFIX.\n\
+The version control method may be selected via the --backup option or
through\n\
+the VERSION_CONTROL environment variable. Here are the values:\n"),
+ NULL,
+ argmatch_backup_docs,
+ argmatch_backup_args
+};
+
int
main (int argc, char *argv[])
{
+#define CHECK(Input, Output) \
+ do { \
+ ASSERT (ARGMATCH (Input, backup_args, backup_vals) == Output); \
+ ASSERT (argmatch_backup_match (Input) == Output); \
+ if (0 <= Output) \
+ ASSERT (*argmatch_backup_xmatch ("test", Input) \
+ == argmatch_backup_args[Output].val); \
+ } while (0)
+
/* Not found. */
- ASSERT (ARGMATCH ("klingon", backup_args, backup_vals) == -1);
+ CHECK ("klingon", -1);
/* Exact match. */
- ASSERT (ARGMATCH ("none", backup_args, backup_vals) == 1);
- ASSERT (ARGMATCH ("nil", backup_args, backup_vals) == 7);
+ CHECK ("none", 1);
+ CHECK ("nil", 7);
/* Too long. */
- ASSERT (ARGMATCH ("nilpotent", backup_args, backup_vals) == -1);
+ CHECK ("nilpotent", -1);
/* Abbreviated. */
- ASSERT (ARGMATCH ("simpl", backup_args, backup_vals) == 3);
- ASSERT (ARGMATCH ("simp", backup_args, backup_vals) == 3);
- ASSERT (ARGMATCH ("sim", backup_args, backup_vals) == 3);
+ CHECK ("simpl", 3);
+ CHECK ("simp", 3);
+ CHECK ("sim", 3);
/* Exact match and abbreviated. */
- ASSERT (ARGMATCH ("numbered", backup_args, backup_vals) == 9);
- ASSERT (ARGMATCH ("numbere", backup_args, backup_vals) == -2);
- ASSERT (ARGMATCH ("number", backup_args, backup_vals) == -2);
- ASSERT (ARGMATCH ("numbe", backup_args, backup_vals) == -2);
- ASSERT (ARGMATCH ("numb", backup_args, backup_vals) == -2);
- ASSERT (ARGMATCH ("num", backup_args, backup_vals) == -2);
- ASSERT (ARGMATCH ("nu", backup_args, backup_vals) == -2);
- ASSERT (ARGMATCH ("n", backup_args, backup_vals) == -2);
+ CHECK ("numbered", 9);
+ CHECK ("numbere", -2);
+ CHECK ("number", -2);
+ CHECK ("numbe", -2);
+ CHECK ("numb", -2);
+ CHECK ("num", -2);
+ CHECK ("nu", -2);
+ CHECK ("n", -2);
/* Ambiguous abbreviated. */
- ASSERT (ARGMATCH ("ne", backup_args, backup_vals) == -2);
+ CHECK ("ne", -2);
+
+ /* Ambiguous abbreviated, but same value ("single" and "simple"). */
+ CHECK ("si", 3);
+ CHECK ("s", 3);
+#undef CHECK
- /* Ambiguous abbreviated, but same value. */
- ASSERT (ARGMATCH ("si", backup_args, backup_vals) == 3);
- ASSERT (ARGMATCH ("s", backup_args, backup_vals) == 3);
+ argmatch_backup_usage (stdout);
return 0;
}
==============================================================
commit 357c9c44882bfb56ce13653dd36e92ac7ae867e1
Author: Akim Demaille <address@hidden>
Date: Sun May 19 19:58:29 2019 +0200
WIP: gnulib: update
diff --git a/gnulib b/gnulib
index d654989d..dcf15176 160000
--- a/gnulib
+++ b/gnulib
@@ -1 +1 @@
-Subproject commit d654989d8bad1a82c4dcbd80204f20147408106e
+Subproject commit dcf15176feb85d0d5ea09b66f176dc9ab9bb6476
diff --git a/m4/.gitignore b/m4/.gitignore
index b31e1f42..0d5b7472 100644
--- a/m4/.gitignore
+++ b/m4/.gitignore
@@ -7,9 +7,7 @@
/calloc.m4
/canonicalize.m4
/clock_time.m4
-/close-stream.m4
/close.m4
-/closeout.m4
/codeset.m4
/config-h.m4
/configmake.m4
diff --git a/src/complain.c b/src/complain.c
index 19c03cc3..d628e9d8 100644
--- a/src/complain.c
+++ b/src/complain.c
@@ -144,6 +144,56 @@ static const warnings warnings_types[] =
ARGMATCH_VERIFY (warnings_args, warnings_types);
+ARGMATCH_DEFINE_GROUP(warning, warnings);
+
+static const argmatch_warning_doc argmatch_warning_docs[] =
+{
+ { "conflicts-sr", N_("S/R conflicts (enabled by default)") },
+ { "conflicts-rr", N_("R/R conflicts (enabled by default)") },
+ { "deprecated", N_("obsolete constructs") },
+ { "empty-rule", N_("empty rules without %empty") },
+ { "midrule-values", N_("unset or unused midrule values") },
+ { "precedence", N_("useless precedence and associativity") },
+ { "yacc", N_("incompatibilities with POSIX Yacc") },
+ { "other", N_("all other warnings (enabled by default)") },
+ { "all", N_("all the warnings except 'yacc'") },
+ { "no-CATEGORY", N_("turn off warnings in CATEGORY") },
+ { "none", N_("turn off all the warnings") },
+ { "error[=CATEGORY]", N_("treat warnings as errors") },
+ { NULL, NULL }
+};
+
+static const argmatch_warning_arg argmatch_warning_args[] =
+{
+ { "none", Wnone },
+ { "midrule-values", Wmidrule_values },
+ { "yacc", Wyacc },
+ { "conflicts-sr", Wconflicts_sr },
+ { "conflicts-rr", Wconflicts_rr },
+ { "deprecated", Wdeprecated },
+ { "empty-rule", Wempty_rule },
+ { "precedence", Wprecedence },
+ { "other", Wother },
+ { "all", Wall },
+ { "error", Werror },
+ { "everything", Weverything },
+ { NULL, Wnone }
+};
+
+const argmatch_warning_group_type argmatch_warning_group =
+{
+ N_("Warning categories include:"),
+ NULL,
+ argmatch_warning_docs,
+ argmatch_warning_args
+};
+
+void
+warning_usage (FILE *out)
+{
+ argmatch_warning_usage (out);
+}
+
void
warning_argmatch (char const *arg, size_t no, size_t err)
{
diff --git a/src/complain.h b/src/complain.h
index 9028b93a..fe7cfdc7 100644
--- a/src/complain.h
+++ b/src/complain.h
@@ -60,6 +60,8 @@ typedef enum
/** Whether -Werror was set. */
extern bool warnings_are_errors;
+void warning_usage (FILE *out);
+
/** Decode a single argument from -W.
*
* \param arg the subarguments to decode.
diff --git a/src/fixits.c b/src/fixits.c
index 97c50569..ee21789e 100644
--- a/src/fixits.c
+++ b/src/fixits.c
@@ -91,7 +91,7 @@ fixits_register (location const *loc, char const* fix)
true);
fixit *f = fixit_new (loc, fix);
gl_sortedlist_add (fixits, (gl_listelement_compar_fn) fixit_cmp, f);
- if (feature_flag & feature_fixit_parsable)
+ if (feature_flag & feature_fixit)
fixit_print (f, stderr);
}
diff --git a/src/getargs.c b/src/getargs.c
index e150162a..e8214726 100644
--- a/src/getargs.c
+++ b/src/getargs.c
@@ -70,26 +70,26 @@ struct bison_language const *language = &valid_languages[0];
/** Decode an option's key.
*
- * \param opt option being decoded.
- * \param keys array of valid subarguments.
- * \param values array of corresponding (int) values.
- * \param all the all value.
- * \param flags the flags to update
- * \param arg the subarguments to decode.
- * If null, then activate all the flags.
- * \param no length of the potential "no-" prefix.
- * Can be 0 or 3. If 3, negate the action of the subargument.
+ * \param opt option being decoded.
+ * \param xargmatch matching function.
+ * \param all the value of the argument 'all'.
+ * \param flags the flags to update
+ * \param arg the subarguments to decode.
+ * If null, then activate all the flags.
+ * \param no length of the potential "no-" prefix.
+ * Can be 0 or 3. If 3, negate the action of the
subargument.
*
* If VALUE != 0 then KEY sets flags and no-KEY clears them.
* If VALUE == 0 then KEY clears all flags from \c all and no-KEY sets all
* flags from \c all. Thus no-none = all and no-all = none.
*/
+typedef int* (xargmatch_fn) (const char *context, const char *arg);
+
static void
-flag_argmatch (const char *opt,
- const char *const keys[], const int values[],
+flag_argmatch (const char *opt, xargmatch_fn xargmatch,
int all, int *flags, char *arg, size_t no)
{
- int value = XARGMATCH (opt, arg + no, keys, values);
+ int value = *xargmatch (opt, arg + no);
/* -rnone == -rno-all, and -rno-none == -rall. */
if (!value)
@@ -106,25 +106,24 @@ flag_argmatch (const char *opt,
/** Decode an option's set of keys.
*
- * \param opt option being decoded (e.g., --report).
- * \param keys array of valid subarguments.
- * \param values array of corresponding (int) values.
- * \param all the all value.
- * \param flags the flags to update
- * \param args comma separated list of effective subarguments to decode.
- * If 0, then activate all the flags.
+ * \param opt option being decoded (e.g., --report).
+ * \param xargmatch matching function.
+ * \param all the value of the argument 'all'.
+ * \param flags the flags to update
+ * \param args comma separated list of effective subarguments to decode.
+ * If 0, then activate all the flags.
*/
static void
flags_argmatch (const char *opt,
- const char * const keys[], const int values[],
+ xargmatch_fn xargmatch,
int all, int *flags, char *args)
{
if (args)
for (args = strtok (args, ","); args; args = strtok (NULL, ","))
{
size_t no = STRPREFIX_LIT ("no-", args) ? 3 : 0;
- flag_argmatch (opt, keys,
- values, all, flags, args, no);
+ flag_argmatch (opt, xargmatch,
+ all, flags, args, no);
}
else
*flags |= all;
@@ -141,8 +140,8 @@ flags_argmatch (const char *opt,
* \arg FlagName_types the list of values.
* \arg FlagName_flag the flag to update.
*/
-#define FLAGS_ARGMATCH(FlagName, Args, All) \
- flags_argmatch ("--" #FlagName, FlagName ## _args, FlagName ## _types, \
+#define FLAGS_ARGMATCH(FlagName, Args, All) \
+ flags_argmatch ("--" #FlagName, argmatch_## FlagName ## _xmatch, \
All, &FlagName ## _flag, Args)
@@ -150,108 +149,132 @@ flags_argmatch (const char *opt,
| --report's handling. |
`----------------------*/
-static const char * const report_args[] =
+ARGMATCH_DEFINE_GROUP(report, enum report);
+
+static const argmatch_report_doc argmatch_report_docs[] =
{
- /* In a series of synonyms, present the most meaningful first, so
- that argmatch_valid be more readable. */
- "none",
- "state", "states",
- "itemset", "itemsets",
- "lookahead", "lookaheads", "look-ahead",
- "solved",
- "all",
- 0
+ { "states", N_("describe the states") },
+ { "itemsets", N_("complete the core item sets with their closure") },
+ { "lookaheads", N_("explicitly associate lookahead tokens to items") },
+ { "solved", N_("describe shift/reduce conflicts solving") },
+ { "all", N_("include all the above information") },
+ { "none", N_("disable the report") },
+ { NULL, NULL },
};
-static const int report_types[] =
+static const argmatch_report_arg argmatch_report_args[] =
{
- report_none,
- report_states, report_states,
- report_states | report_itemsets, report_states | report_itemsets,
- report_states | report_lookahead_tokens,
- report_states | report_lookahead_tokens,
- report_states | report_lookahead_tokens,
- report_states | report_solved_conflicts,
- report_all
+ { "none", report_none },
+ { "states", report_states },
+ { "itemsets", report_states | report_itemsets },
+ { "lookaheads", report_states | report_lookahead_tokens },
+ { "look-ahead", report_states | report_lookahead_tokens },
+ { "solved", report_states | report_solved_conflicts },
+ { "all", report_all },
+ { NULL, report_none },
};
-ARGMATCH_VERIFY (report_args, report_types);
-
+const argmatch_report_group_type argmatch_report_group =
+{
+ N_("THINGS is a list of comma separated words that can include:"),
+ NULL,
+ argmatch_report_docs,
+ argmatch_report_args
+};
/*---------------------.
| --trace's handling. |
`---------------------*/
-static const char * const trace_args[] =
+ARGMATCH_DEFINE_GROUP(trace, enum trace);
+
+static const argmatch_trace_doc argmatch_trace_docs[] =
{
- "none - no traces",
- "locations - full display of the locations",
- "scan - grammar scanner traces",
- "parse - grammar parser traces",
- "automaton - construction of the automaton",
- "bitsets - use of bitsets",
- "closure - input/output of closure",
- "grammar - reading, reducing the grammar",
- "resource - memory consumption (where available)",
- "sets - grammar sets: firsts, nullable etc.",
- "muscles - m4 definitions passed to the skeleton",
- "tools - m4 invocation",
- "m4 - m4 traces",
- "skeleton - skeleton postprocessing",
- "time - time consumption",
- "ielr - IELR conversion",
- "all - all of the above",
- 0
+ { "none", N_("no traces") },
+ { "locations", N_("full display of the locations") },
+ { "scan", N_("grammar scanner traces") },
+ { "parse", N_("grammar parser traces") },
+ { "automaton", N_("construction of the automaton") },
+ { "bitsets", N_("use of bitsets") },
+ { "closure", N_("input/output of closure") },
+ { "grammar", N_("reading, reducing the grammar") },
+ { "resource", N_("memory consumption (where available)") },
+ { "sets", N_("grammar sets: firsts, nullable etc.") },
+ { "muscles", N_("m4 definitions passed to the skeleton") },
+ { "tools", N_("m4 invocation") },
+ { "m4", N_("m4 traces") },
+ { "skeleton", N_("skeleton postprocessing") },
+ { "time", N_("time consumption") },
+ { "ielr", N_("IELR conversion") },
+ { "all", N_("all of the above") },
+ { NULL, NULL},
};
-static const int trace_types[] =
+static const argmatch_trace_arg argmatch_trace_args[] =
{
- trace_none,
- trace_locations,
- trace_scan,
- trace_parse,
- trace_automaton,
- trace_bitsets,
- trace_closure,
- trace_grammar,
- trace_resource,
- trace_sets,
- trace_muscles,
- trace_tools,
- trace_m4,
- trace_skeleton,
- trace_time,
- trace_ielr,
- trace_all
+ { "none", trace_none },
+ { "locations", trace_locations },
+ { "scan", trace_scan },
+ { "parse", trace_parse },
+ { "automaton", trace_automaton },
+ { "bitsets", trace_bitsets },
+ { "closure", trace_closure },
+ { "grammar", trace_grammar },
+ { "resource", trace_resource },
+ { "sets", trace_sets },
+ { "muscles", trace_muscles },
+ { "tools", trace_tools },
+ { "m4", trace_m4 },
+ { "skeleton", trace_skeleton },
+ { "time", trace_time },
+ { "ielr", trace_ielr },
+ { "all", trace_all },
+ { NULL, trace_none},
};
-ARGMATCH_VERIFY (trace_args, trace_types);
-
+const argmatch_trace_group_type argmatch_trace_group =
+{
+ N_("TRACES is a list of comma separated words that can include:"),
+ NULL,
+ argmatch_trace_docs,
+ argmatch_trace_args
+};
/*-----------------------.
| --feature's handling. |
`-----------------------*/
-static const char * const feature_args[] =
+ARGMATCH_DEFINE_GROUP(feature, enum feature);
+
+static const argmatch_feature_doc argmatch_feature_docs[] =
{
- "none",
- "caret", "diagnostics-show-caret",
- "fixit", "diagnostics-parseable-fixits",
- "syntax-only",
- "all",
- 0
+ { "caret", N_("show errors with carets") },
+ { "fixit", N_("show machine-readable fixes") },
+ { "syntax-only", N_("do not generate any file") },
+ { "all", N_("all of the above") },
+ { "none", N_("disable all of the above") },
+ { NULL, NULL }
};
-static const int feature_types[] =
+static const argmatch_feature_arg argmatch_feature_args[] =
{
- feature_none,
- feature_caret, feature_caret,
- feature_fixit_parsable, feature_fixit_parsable,
- feature_syntax_only,
- feature_all
+ { "none", feature_none },
+ { "caret", feature_caret },
+ { "diagnostics-show-caret", feature_caret },
+ { "fixit", feature_fixit },
+ { "diagnostics-parseable-fixits", feature_fixit },
+ { "syntax-only", feature_syntax_only },
+ { "all", feature_all },
+ { NULL, feature_none}
};
-ARGMATCH_VERIFY (feature_args, feature_types);
+const argmatch_feature_group_type argmatch_feature_group =
+{
+ N_("FEATURES is a list of comma separated words that can include:"),
+ NULL,
+ argmatch_feature_docs,
+ argmatch_feature_args
+};
/*-------------------------------------------.
| Display the help message and exit STATUS. |
@@ -334,49 +357,18 @@ Output:\n\
"), stdout);
putc ('\n', stdout);
- fputs (_("\
-Warning categories include:\n\
- 'conflicts-sr' S/R conflicts (enabled by default)\n\
- 'conflicts-rr' R/R conflicts (enabled by default)\n\
- 'deprecated' obsolete constructs\n\
- 'empty-rule' empty rules without %empty\n\
- 'midrule-values' unset or unused midrule values\n\
- 'precedence' useless precedence and associativity\n\
- 'yacc' incompatibilities with POSIX Yacc\n\
- 'other' all other warnings (enabled by default)\n\
- 'all' all the warnings except 'yacc'\n\
- 'no-CATEGORY' turn off warnings in CATEGORY\n\
- 'none' turn off all the warnings\n\
- 'error[=CATEGORY]' treat warnings as errors\n\
-"), stdout);
+ warning_usage (stdout);
putc ('\n', stdout);
- fputs (_("\
-THINGS is a list of comma separated words that can include:\n\
- 'state' describe the states\n\
- 'itemset' complete the core item sets with their closure\n\
- 'lookahead' explicitly associate lookahead tokens to items\n\
- 'solved' describe shift/reduce conflicts solving\n\
- 'all' include all the above information\n\
- 'none' disable the report\n\
-"), stdout);
+ argmatch_report_usage (stdout);
putc ('\n', stdout);
- fputs (_("\
-FEATURES is a list of comma separated words that can include:\n\
- 'caret', 'diagnostics-show-caret'\n\
- show errors with carets\n\
- 'fixit', 'diagnostics-parseable-fixits'\n\
- show machine-readable fixes\n\
- 'syntax-only'\n\
- do not generate any file\n\
- 'all'\n\
- all of the above\n\
- 'none'\n\
- disable all of the above\n\
- "), stdout);
+ argmatch_feature_usage (stdout);
+ putc ('\n', stdout);
+ argmatch_trace_usage (stdout);
putc ('\n', stdout);
+
printf (_("Report bugs to <%s>.\n"), PACKAGE_BUGREPORT);
printf (_("%s home page: <%s>.\n"), PACKAGE_NAME, PACKAGE_URL);
fputs (_("General help using GNU software: "
diff --git a/src/getargs.h b/src/getargs.h
index 0cc4daed..ab6af6c3 100644
--- a/src/getargs.h
+++ b/src/getargs.h
@@ -118,7 +118,7 @@ enum feature
{
feature_none = 0, /**< No additional feature. */
feature_caret = 1 << 0, /**< Output errors with carets. */
- feature_fixit_parsable = 1 << 1, /**< Issue instructions to fix the
sources. */
+ feature_fixit = 1 << 1, /**< Issue instructions to fix the
sources. */
feature_syntax_only = 1 << 2, /**< Don't generate output. */
feature_all = ~0 /**< All above features. */
};
- Re: argmatch: accept perfect matches in documented arglists, (continued)
- Re: argmatch: accept perfect matches in documented arglists, Bruno Haible, 2019/05/05
- Re: argmatch: accept perfect matches in documented arglists, Akim Demaille, 2019/05/19
- Re: argmatch: accept perfect matches in documented arglists, Bruno Haible, 2019/05/19
- Re: argmatch: accept perfect matches in documented arglists, Akim Demaille, 2019/05/19
- Re: argmatch: accept perfect matches in documented arglists, Bruno Haible, 2019/05/19
- Re: argmatch: accept perfect matches in documented arglists, Akim Demaille, 2019/05/19
- Re: argmatch: accept perfect matches in documented arglists, Akim Demaille, 2019/05/19
- Re: argmatch: accept perfect matches in documented arglists, Bruno Haible, 2019/05/19
- Re: argmatch: accept perfect matches in documented arglists, Paul Eggert, 2019/05/19
- Re: argmatch: accept perfect matches in documented arglists, Akim Demaille, 2019/05/19
- Re: argmatch: accept perfect matches in documented arglists,
Akim Demaille <=
- Re: argmatch: accept perfect matches in documented arglists, Paul Eggert, 2019/05/21
- Re: argmatch: accept perfect matches in documented arglists, Akim Demaille, 2019/05/22
- Re: argmatch: accept perfect matches in documented arglists, Bruno Haible, 2019/05/22
- Re: argmatch: accept perfect matches in documented arglists, Akim Demaille, 2019/05/25