bug-gnulib
[Top][All Lists]
Advanced

[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.  */
   };




reply via email to

[Prev in Thread] Current Thread [Next in Thread]