[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: --report
From: |
Akim Demaille |
Subject: |
Re: --report |
Date: |
25 May 2002 17:26:39 +0200 |
User-agent: |
Gnus/5.0808 (Gnus v5.8.8) XEmacs/21.4 (Honest Recruiter) |
>>>>> "Paul" == Paul Eggert <address@hidden> writes:
>> I prefer too, but I've always been told, in particular in
>> scientific papers, to promote passive :(
Paul> Yes, scientific text often prefers the passive voice, [...]
Thanks for all this information Paul.
>> Arg. We disagree on the way to express gotos. To me, it is an
>> heresy to name `exp' a lookahead here, both from the theoretical
>> point of view, and the implementation point of view.
Paul> Fair enough. But I still didn't understand the text. Perhaps
Paul> you might want to reread it with my confusion in mind.
I'm appending below the patch I'm installing. I hope it will be
clearer. [Arg, I had not realized the test suite needs to be adjusted
to, so it will be applied once I'm fully done.] I'm installing it
since it will then be easier to discuss small changes to it.
>> | Better yet, I would use an enum,
>>
>> I refrain using enums when I play bitwise tricks. In such a
>> framework, the advantage of enums vs #defines is unclear to me (as,
>> I doubt that debuggers are able to report the value `a|b' as the
>> string `a|b' in case of enums. Put after all, why not.).
Paul> Many debuggers can't see defines, so you can't use such a
Paul> debugger to print the value of an expression that involves a
Paul> defined symbol.
I must have been unclear: precisely thinking about debuggers, because
I know no debugger that with
enum foo { a = 1, b = 2 };
foo bar = 3;
would report bar as a | b, I feel less attracted by enums.
[...]
Paul> There are of course some advantages to the Pascal-like rules,
Paul> but the modern consensus is that C enums are just integers, and
Paul> we might as well use that consensus rather than fight it.
:)
I hear you 5/5 :)
Index: ChangeLog
from Akim Demaille <address@hidden>
* doc/bison.texinfo (Debugging): Split into...
(Tracing): this new section, its former contents, and...
(Understanding): this new section.
* src/getargs.h, src/getargs.c (verbose_flag): Remove, replaced
by...
(report_flag): this.
Adjust all dependencies.
(report_args, report_types, report_argmatch): New.
(usage, getargs): Report/support -r, --report.
* src/options.c (option_table): For the time being disable
%verbose.
Support --report/-r, so remove -r from the obsolete --raw.
* src/print.c: Attach full item sets and lookaheads reports to
report_flag instead of trace_flag.
* lib/argmatch.h, lib/argmatch.c: New, from Fileutils 4.1.
Index: NEWS
--- NEWS Mon, 06 May 2002 19:46:46 +0200 akim
+++ NEWS Sat, 25 May 2002 17:03:47 +0200 akim
@@ -72,6 +72,13 @@
the compiler supports ANSI C or is a C++ compiler, as enums.
This helps debuggers producing symbols instead of values.
+* Reports
+ In addition to --verbose, bison supports --report=THINGS, which
+ produces additional information:
+
+ `itemset' complete the core item sets with their closure
+ `lookahead' explicitly associate lookaheads to items
+
Changes in version 1.35, 2002-03-25:
Index: TODO
--- TODO Wed, 08 May 2002 11:29:49 +0200 akim
+++ TODO Sat, 25 May 2002 17:08:54 +0200 akim
@@ -1,5 +1,23 @@
-*- outline -*-
+* documentation
+Explain $axiom (and maybe change its name: BTYacc names it goal).
+Complete the glossary (item, axiom, ?).
+
+* report documentation
+Extend with error. The hard part will probably be finding the right
+rule so that a single state does not exhibit to many yet undocumented
+``features''. Maybe an empty action ought to be presented too. Shall
+we try to make a single grammar with all these features, or should we
+have several very small grammars?
+
+* documentation
+Some history of Bison and some bibliography would be most welcome.
+Are there any Texinfo standards for bibliography?
+
+* %verbose
+under what form should it be kept?
+
* Several %unions
I think this is a pleasant (but useless currently) feature, but in the
future, I want a means to %include other bits of grammars, and _then_
@@ -21,130 +39,25 @@
char *sval;
}
-* Experimental report features
-Decide whether they should be enabled, or optional. For instance, on:
-
- input:
- exp
- | input exp
- ;
-
- exp:
- token1 "1"
- | token2 "2"
- | token3 "3"
- ;
-
- token1: token;
- token2: token;
- token3: token;
-
-the traditional Bison reports:
-
- state 0
-
- $axiom -> . input $ (rule 0)
-
- token shift, and go to state 1
-
- input go to state 2
- exp go to state 3
- token1 go to state 4
- token2 go to state 5
- token3 go to state 6
-
- state 1
-
- token1 -> token . (rule 6)
- token2 -> token . (rule 7)
- token3 -> token . (rule 8)
-
- "2" reduce using rule 7 (token2)
- "3" reduce using rule 8 (token3)
- $default reduce using rule 6 (token1)
-
-while with --trace, i.e., when enabling both the display of non-core
-item sets and the display of lookaheads, Bison now displays:
-
- state 0
-
- $axiom -> . input $ (rule 0)
- input -> . exp (rule 1)
- input -> . input exp (rule 2)
- exp -> . token1 "1" (rule 3)
- exp -> . token2 "2" (rule 4)
- exp -> . token3 "3" (rule 5)
- token1 -> . token (rule 6)
- token2 -> . token (rule 7)
- token3 -> . token (rule 8)
-
- token shift, and go to state 1
-
- input go to state 2
- exp go to state 3
- token1 go to state 4
- token2 go to state 5
- token3 go to state 6
-
- state 1
-
- token1 -> token . ["1"] (rule 6)
- token2 -> token . ["2"] (rule 7)
- token3 -> token . ["3"] (rule 8)
-
- "2" reduce using rule 7 (token2)
- "3" reduce using rule 8 (token3)
- $default reduce using rule 6 (token1)
-
-so decide whether this should be an option, or always enabled. I'm in
-favor of making it the default, but maybe we should tune the output to
-distinguish core item sets from non core:
-
- state 0
- Core:
- $axiom -> . input $ (rule 0)
-
- Derived:
- input -> . exp (rule 1)
- input -> . input exp (rule 2)
- exp -> . token1 "1" (rule 3)
- exp -> . token2 "2" (rule 4)
- exp -> . token3 "3" (rule 5)
- token1 -> . token (rule 6)
- token2 -> . token (rule 7)
- token3 -> . token (rule 8)
-
- token shift, and go to state 1
-
- input go to state 2
- exp go to state 3
- token1 go to state 4
- token2 go to state 5
- token3 go to state 6
-
-
-> So, it seems clear that it has to be an additional option :)
-
-Paul:
-
- There will be further such options in the future, so I'd make
- them all operands of the --report option. E.g., you could do
- something like this:
+* --report=conflict-path
+Provide better assistance for understanding the conflicts by providing
+a sample text exhibiting the (LALR) ambiguity.
- --report=state --report=lookahead --report=itemset
- --report=conflict-path
+* report
+Solved conflicts should not be reported in the beginning of the file.
+Rather they should be reported within each state description. Also,
+now that the symbol providing the precedence of a rule is kept, it is
+possible to explain why a conflict was solved this way. E.g., instead
+of
- where "--verbose" is equivalent to "--report=state", and where
- "--report=conflict-path" reports each path to a conflict
- state.
+ Conflict in state 8 between rule 2 and token '+' resolved as reduce.
- (As a minor point, I prefer avoiding plurals in option names.
- It's partly for brevity, and partly to avoid wearing out the
- 's' keys in our keyboards. :-)
+we can (in state 8) report something like
-To implement this, see in the Fileutils the latest versions of
-argmatch and so forth.
+ Conflict between rule 2 and token '+' resolved as reduce
+ because '*' < '+'.
+or something like that.
* Coding system independence
Paul notes:
Index: doc/bison.texinfo
--- doc/bison.texinfo Thu, 02 May 2002 19:30:29 +0200 akim
+++ doc/bison.texinfo Sat, 25 May 2002 16:49:50 +0200 akim
@@ -5,9 +5,7 @@
@settitle Bison @value{VERSION}
@setchapternewpage odd
address@hidden
@finalout
address@hidden iftex
@c SMALL BOOK version
@c This edition has been formatted so that you can format and print it in
@@ -23,6 +21,7 @@
@c Check COPYRIGHT dates. should be updated in the titlepage, ifinfo
@c titlepage; should NOT be changed in the GPL. --mew
address@hidden FIXME: I don't understand this `iftex'. Obsolete? --akim.
@iftex
@syncodeindex fn cp
@syncodeindex vr cp
@@ -154,7 +153,7 @@ @node Top
* Error Recovery:: Writing rules for error recovery.
* Context Dependency:: What to do if your language syntax is too
messy for Bison to handle straightforwardly.
-* Debugging:: Debugging Bison parsers that parse wrong.
+* Debugging:: Understanding or debugging Bison parsers.
* Invocation:: How to run Bison (to produce the parser source file).
* Table of Symbols:: All the keywords of the Bison language are explained.
* Glossary:: Basic concepts are explained.
@@ -299,6 +298,11 @@ @node Top
* Tie-in Recovery:: Lexical tie-ins have implications for how
error recovery rules must be written.
+Understanding or Debugging Your Parser
+
+* Understanding:: Understanding the structure of your parser.
+* Tracing:: Tracing the execution of your parser.
+
Invoking Bison
* Bison Options:: All the options described in detail,
@@ -707,9 +711,9 @@ @node Bison Parser
those cases your code should respect the identifiers reserved by those
headers. On some address@hidden hosts, @code{<alloca.h>},
@code{<stddef.h>}, and @code{<stdlib.h>} are included as needed to
-declare memory allocators and related types.
-Other system headers may be included if you define @code{YYDEBUG} to a
-nonzero value (@pxref{Debugging, ,Debugging Your Parser}).
+declare memory allocators and related types. Other system headers may
+be included if you define @code{YYDEBUG} to a nonzero value
+(@pxref{Tracing, ,Tracing Your Parser}).
@node Stages
@section Stages in Using Bison
@@ -2351,14 +2355,14 @@ expseq1: exp
@end example
@noindent
-Any kind of sequence can be defined using either left recursion or
-right recursion, but you should always use left recursion, because it
-can parse a sequence of any number of elements with bounded stack
-space. Right recursion uses up space on the Bison stack in proportion
-to the number of elements in the sequence, because all the elements
-must be shifted onto the stack before the rule can be applied even
-once. @xref{Algorithm, ,The Bison Parser Algorithm }, for
-further explanation of this.
+Any kind of sequence can be defined using either left recursion or right
+recursion, but you should always use left recursion, because it can
+parse a sequence of any number of elements with bounded stack space.
+Right recursion uses up space on the Bison stack in proportion to the
+number of elements in the sequence, because all the elements must be
+shifted onto the stack before the rule can be applied even once.
address@hidden, ,The Bison Parser Algorithm}, for further explanation
+of this.
@cindex mutual recursion
@dfn{Indirect} or @dfn{mutual} recursion occurs when the result of the
@@ -3276,7 +3280,7 @@ directives:
@item %debug
In the parser file, define the macro @code{YYDEBUG} to 1 if it is not
already defined, so that the debugging facilities are compiled.
address@hidden, ,Debugging Your Parser}.
address@hidden, ,Tracing Your Parser}.
@item %defines
Write an extra output file containing macro definitions for the token
@@ -3386,17 +3390,10 @@ directives:
@item %verbose
Write an extra output file containing verbose descriptions of the
parser states and what is done for each type of look-ahead token in
-that state.
-
-This file also describes all the conflicts, both those resolved by
-operator precedence and the unresolved ones.
+that state. @xref{Understanding, , Understanding Your Parser}, for more
+information.
-The file's name is made by removing @samp{.tab.c} or @samp{.c} from
-the parser output file name, and adding @samp{.output} instead.
-Therefore, if the input file is @file{foo.y}, then the parser file is
-called @file{foo.tab.c} by default. As a consequence, the verbose
-output file is called @file{foo.output}.
@item %yacc
Pretend the option @option{--yacc} was given, i.e., imitate Yacc,
@@ -4954,8 +4951,414 @@ expr: @dots{}
be such that you can be sure that it always will, or always won't, have to
clear the flag.
address@hidden ================================================== Debugging
Your Parser
+
@node Debugging
@chapter Debugging Your Parser
+
+Developing a parser can be a challenge, especially if you don't
+understand the algorithm (@pxref{Algorithm, ,The Bison Parser
+Algorithm}). Even so, sometimes a detailed description of the automaton
+can help (@pxref{Understanding, , Understanding Your Parser}), or
+tracing the execution of the parser can give some insight on why it
+behaves improperly (@pxref{Tracing, , Tracing Your Parser}).
+
address@hidden
+* Understanding:: Understanding the structure of your parser.
+* Tracing:: Tracing the execution of your parser.
address@hidden menu
+
address@hidden Understanding
address@hidden Understanding Your Parser
+
+As documented elsewhere (@pxref{Algorithm, ,The Bison Parser Algorithm})
+Bison parsers are @dfn{shift/reduce automata}. In some cases (much more
+frequent than one would hope), looking at this automaton is required to
+tune or simply fix a parser. Bison provides two different
+representation of it, either textually or graphically (as a @sc{vcg}
+file).
+
+The textual file is generated when the options @option{--report} or
address@hidden are specified, see @xref{Invocation, , Invoking
+Bison}. Its name is made by removing @samp{.tab.c} or @samp{.c} from
+the parser output file name, and adding @samp{.output} instead.
+Therefore, if the input file is @file{foo.y}, then the parser file is
+called @file{foo.tab.c} by default. As a consequence, the verbose
+output file is called @file{foo.output}.
+
+The following grammar file, @file{calc.y}, will be used in the sequel:
+
address@hidden
+%token NUM STR
+%left '+' '-'
+%left '*'
+%%
+exp: exp '+' exp
+ | exp '-' exp
+ | exp '*' exp
+ | exp '/' exp
+ | NUM
+ ;
+useless: STR;
+%%
address@hidden example
+
address@hidden reports that @samp{calc.y contains 1 useless nonterminal
+and 1 useless rule} and that @samp{calc.y contains 7 shift/reduce
+conflicts}. When given @option{--report=state}, in addition to
address@hidden, it creates a file @file{calc.output} with contents
+detailed below. The order of the output and the exact presentation
+might vary, but the interpretation is the same.
+
+The first section includes details on conflicts that were solved thanks
+to precedence and/or associativity:
+
address@hidden
+Conflict in state 8 between rule 2 and token '+' resolved as reduce.
+Conflict in state 8 between rule 2 and token '-' resolved as reduce.
+Conflict in state 8 between rule 2 and token '*' resolved as shift.
address@hidden @dots{}
address@hidden example
+
address@hidden
+The next section lists states that still have conflicts.
+
address@hidden
+State 8 contains 1 shift/reduce conflict.
+State 9 contains 1 shift/reduce conflict.
+State 10 contains 1 shift/reduce conflict.
+State 11 contains 4 shift/reduce conflicts.
address@hidden example
+
address@hidden
address@hidden token, useless
address@hidden useless token
address@hidden nonterminal, useless
address@hidden useless nonterminal
address@hidden rule, useless
address@hidden useless rule
+The next section reports useless tokens, nonterminal and rules. Useless
+nonterminals and rules are removed in order to produce a smaller parser,
+but useless tokens are preserved, since they might be used by the
+scanner (note the difference between ``useless'' and ``not used''
+below):
+
address@hidden
+Useless nonterminals:
+ useless
+
+Terminals which are not used:
+ STR
+
+Useless rules:
+#6 useless: STR;
address@hidden example
+
address@hidden
+The next section reproduces the exact grammar that Bison used:
+
address@hidden
+Grammar
+
+ Number, Line, Rule
+ 0 5 $axiom -> exp $
+ 1 5 exp -> exp '+' exp
+ 2 6 exp -> exp '-' exp
+ 3 7 exp -> exp '*' exp
+ 4 8 exp -> exp '/' exp
+ 5 9 exp -> NUM
address@hidden example
+
address@hidden
+and reports the uses of the symbols:
+
address@hidden
+Terminals, with rules where they appear
+
+$ (0) 0
+'*' (42) 3
+'+' (43) 1
+'-' (45) 2
+'/' (47) 4
+error (256)
+NUM (258) 5
+
+Nonterminals, with rules where they appear
+
+$axiom (8)
+ on left: 0
+exp (9)
+ on left: 1 2 3 4 5, on right: 0 1 2 3 4
address@hidden example
+
address@hidden
address@hidden item
address@hidden pointed rule
address@hidden rule, pointed
+Bison then proceeds onto the automaton itself, describing each state
+with it set of @dfn{items}, also known as @dfn{pointed rules}. Each
+item is a production rule together with a point (marked by @samp{.})
+that the input cursor.
+
address@hidden
+state 0
+
+ $axiom -> . exp $ (rule 0)
+
+ NUM shift, and go to state 1
+
+ exp go to state 2
address@hidden example
+
+This reads as follows: ``state 0 corresponds to being at the very
+beginning of the parsing, in the initial rule, right before the start
+symbol (here, @code{exp}). When the parser returns to this state right
+after having reduced a rule that produced an @code{exp}, the control
+flow jumps to state 2. If there is no such transition on a nonterminal
+symbol, and the lookahead is a @code{NUM}, then this token is shifted on
+the parse stack, and the control flow jumps to state 1. Any other
+lookahead triggers a parse error.''
+
address@hidden core, item set
address@hidden item set core
address@hidden kernel, item set
address@hidden item set core
+Even though the only active rule in state 0 seems to be rule 0, the
+report lists @code{NUM} as a lookahead symbol because @code{NUM} can be
+at the beginning of any rule deriving an @code{exp}. By default Bison
+reports the so-called @dfn{core} or @dfn{kernel} of the item set, but if
+you want to see more detail you can invoke @command{bison} with
address@hidden to list all the items, include those that can
+be derived:
+
address@hidden
+state 0
+
+ $axiom -> . exp $ (rule 0)
+ exp -> . exp '+' exp (rule 1)
+ exp -> . exp '-' exp (rule 2)
+ exp -> . exp '*' exp (rule 3)
+ exp -> . exp '/' exp (rule 4)
+ exp -> . NUM (rule 5)
+
+ NUM shift, and go to state 1
+
+ exp go to state 2
address@hidden example
+
address@hidden
+In the state 1...
+
address@hidden
+state 1
+
+ exp -> NUM . (rule 5)
+
+ $default reduce using rule 5 (exp)
address@hidden example
+
address@hidden
+the rule 5, @samp{exp: NUM;}, is completed. Whatever the lookahead
+(@samp{$default}), the parser will reduce it. If it was coming from
+state 0, then, after this reduction it will return to state 0, and will
+jump to state 2 (@samp{exp: go to state 2}).
+
address@hidden
+state 2
+
+ $axiom -> exp . $ (rule 0)
+ exp -> exp . '+' exp (rule 1)
+ exp -> exp . '-' exp (rule 2)
+ exp -> exp . '*' exp (rule 3)
+ exp -> exp . '/' exp (rule 4)
+
+ $ shift, and go to state 3
+ '+' shift, and go to state 4
+ '-' shift, and go to state 5
+ '*' shift, and go to state 6
+ '/' shift, and go to state 7
address@hidden example
+
address@hidden
+In state 2, the automaton can only shift a symbol. For instance,
+because of the item @samp{exp -> exp . '+' exp}, if the lookahead if
address@hidden, it will be shifted on the parse stack, and the automaton
+control will jump to state 4, corresponding to the item @samp{exp -> exp
+'+' . exp}. Since there is no default action, any other token than
+those listed above will trigger a parse error.
+
+The state 3 is named the @dfn{final state}, or the @dfn{accepting
+state}:
+
address@hidden
+state 3
+
+ $axiom -> exp $ . (rule 0)
+
+ $default accept
address@hidden example
+
address@hidden
+the initial rule is completed (the start symbol and the end
+of input were read), the parsing exits successfully.
+
+The interpretation of states 4 to 7 is straightforward, and is left to
+the reader.
+
address@hidden
+state 4
+
+ exp -> exp '+' . exp (rule 1)
+
+ NUM shift, and go to state 1
+
+ exp go to state 8
+
+state 5
+
+ exp -> exp '-' . exp (rule 2)
+
+ NUM shift, and go to state 1
+
+ exp go to state 9
+
+state 6
+
+ exp -> exp '*' . exp (rule 3)
+
+ NUM shift, and go to state 1
+
+ exp go to state 10
+
+state 7
+
+ exp -> exp '/' . exp (rule 4)
+
+ NUM shift, and go to state 1
+
+ exp go to state 11
address@hidden example
+
+As was announced in beginning of the report, @samp{State 8 contains 1
+shift/reduce conflict}:
+
address@hidden
+state 8
+
+ exp -> exp . '+' exp (rule 1)
+ exp -> exp '+' exp . (rule 1)
+ exp -> exp . '-' exp (rule 2)
+ exp -> exp . '*' exp (rule 3)
+ exp -> exp . '/' exp (rule 4)
+
+ '*' shift, and go to state 6
+ '/' shift, and go to state 7
+
+ '/' [reduce using rule 1 (exp)]
+ $default reduce using rule 1 (exp)
address@hidden example
+
+Indeed, there are two actions associated to the lookahead @samp{/}:
+either shifting (and going to state 7), or reducing rule 1. The
+conflict means that either the grammar is ambiguous, or the parser lacks
+information to make the right decision. Indeed the grammar is
+ambiguous, as, since we did not specify the precedence of @samp{/}, the
+sentence @samp{NUM + NUM / NUM} can be parsed as @samp{NUM + (NUM /
+NUM)}, which corresponds to shifting @samp{/}, or as @samp{(NUM + NUM) /
+NUM}, which corresponds to reducing rule 1.
+
+Because in LALR(1) parsing a single decision can be made, Bison
+arbitrarily chose to disable the reduction, see @ref{Shift/Reduce, ,
+Shift/Reduce Conflicts}. Discarded actions are reported in between
+square brackets.
+
+Note that all the previous states had a single possible action: either
+shifting the next token and going to the corresponding state, or
+reducing a single rule. In the other cases, i.e., when shifting
address@hidden reducing is possible or when @emph{several} reductions are
+possible, the lookahead is required to select the action. State 8 is
+one such state: if the lookahead is @samp{*} or @samp{/} then the action
+is shifting, otherwise the action is reducing rule 1. In other words,
+the first two items, corresponding to rule 1, are not eligible when the
+lookahead is @samp{*}, since we specified that @samp{*} has higher
+precedence that @samp{+}. More generally, some items are eligible only
+with some set of possible lookaheads. When run with
address@hidden, Bison specifies these lookaheads:
+
address@hidden
+state 8
+
+ exp -> exp . '+' exp [$, '+', '-', '/'] (rule 1)
+ exp -> exp '+' exp . [$, '+', '-', '/'] (rule 1)
+ exp -> exp . '-' exp (rule 2)
+ exp -> exp . '*' exp (rule 3)
+ exp -> exp . '/' exp (rule 4)
+
+ '*' shift, and go to state 6
+ '/' shift, and go to state 7
+
+ '/' [reduce using rule 1 (exp)]
+ $default reduce using rule 1 (exp)
address@hidden example
+
+The remaining states are similar:
+
address@hidden
+state 9
+
+ exp -> exp . '+' exp (rule 1)
+ exp -> exp . '-' exp (rule 2)
+ exp -> exp '-' exp . (rule 2)
+ exp -> exp . '*' exp (rule 3)
+ exp -> exp . '/' exp (rule 4)
+
+ '*' shift, and go to state 6
+ '/' shift, and go to state 7
+
+ '/' [reduce using rule 2 (exp)]
+ $default reduce using rule 2 (exp)
+
+state 10
+
+ exp -> exp . '+' exp (rule 1)
+ exp -> exp . '-' exp (rule 2)
+ exp -> exp . '*' exp (rule 3)
+ exp -> exp '*' exp . (rule 3)
+ exp -> exp . '/' exp (rule 4)
+
+ '/' shift, and go to state 7
+
+ '/' [reduce using rule 3 (exp)]
+ $default reduce using rule 3 (exp)
+
+state 11
+
+ exp -> exp . '+' exp (rule 1)
+ exp -> exp . '-' exp (rule 2)
+ exp -> exp . '*' exp (rule 3)
+ exp -> exp . '/' exp (rule 4)
+ exp -> exp '/' exp . (rule 4)
+
+ '+' shift, and go to state 4
+ '-' shift, and go to state 5
+ '*' shift, and go to state 6
+ '/' shift, and go to state 7
+
+ '+' [reduce using rule 4 (exp)]
+ '-' [reduce using rule 4 (exp)]
+ '*' [reduce using rule 4 (exp)]
+ '/' [reduce using rule 4 (exp)]
+ $default reduce using rule 4 (exp)
address@hidden example
+
address@hidden
+Observe that state 11 contains conflicts due to the lack of precedence
+of @samp{/} wrt @samp{+}, @samp{-}, and @samp{*}, but also because the
+associativity of @samp{/} is not specified.
+
+
address@hidden Tracing
address@hidden Tracing Your Parser
@findex yydebug
@cindex debugging
@cindex tracing the parser
@@ -5059,6 +5462,8 @@ @node Debugging
@}
@end smallexample
address@hidden ================================================= Invoking Bison
+
@node Invocation
@chapter Invoking Bison
@cindex invoking Bison
@@ -5158,7 +5563,7 @@ @node Bison Options
@itemx --debug
In the parser file, define the macro @code{YYDEBUG} to 1 if it is not
already defined, so that the debugging facilities are compiled.
address@hidden, ,Debugging Your Parser}.
address@hidden, ,Tracing Your Parser}.
@item --locations
Pretend that @code{%locations} was specified. @xref{Decl Summary}.
@@ -5204,6 +5609,27 @@ @node Bison Options
Pretend that @code{%verbose} was specified, i.e, specify prefix to use
for all Bison output file names. @xref{Decl Summary}.
address@hidden -r @var{things}
address@hidden address@hidden
+Write an extra output file containing verbose description of the comma
+separated list of @var{things} among:
+
address@hidden @code
address@hidden state
+Description of the grammar, conflicts (resolved and unresolved), and
+LALR automaton.
+
address@hidden lookahead
+Implies @code{state} and augments the description of the automaton with
+each rule's lookahead set.
+
address@hidden itemset
+Implies @code{state} and augments the description of the automaton with
+the full set of items for each state, instead of its core only.
address@hidden table
+
+For instance, on the following grammar
+
@item -v
@itemx --verbose
Pretend that @code{%verbose} was specified, i.e, write an extra output
@@ -5365,8 +5791,8 @@ @node Table of Symbols
token. @xref{Action Features, ,Special Features for Use in Actions}.
@item YYDEBUG
-Macro to define to equip the parser with tracing code. @xref{Debugging,
-,Debugging Your Parser}.
+Macro to define to equip the parser with tracing code. @xref{Tracing,
+,Tracing Your Parser}.
@item YYERROR
Macro to pretend that a syntax error has just been detected: call
@@ -5430,7 +5856,7 @@ @node Table of Symbols
@item yydebug
External integer variable set to zero by default. If @code{yydebug}
is given a nonzero value, the parser will output information on input
-symbols and parser action. @xref{Debugging, ,Debugging Your Parser}.
+symbols and parser action. @xref{Tracing, ,Tracing Your Parser}.
@item yyerrok
Macro to cause parser to recover immediately to its normal mode
Index: lib/Makefile.am
--- lib/Makefile.am Wed, 08 May 2002 11:29:49 +0200 akim
+++ lib/Makefile.am Mon, 20 May 2002 18:44:57 +0200 akim
@@ -35,6 +35,7 @@
EXTRA_DIST = malloc.c realloc.c strnlen.c
libbison_a_SOURCES = \
+ argmatch.h argmatch.c \
gettext.h \
basename.c dirname.h dirname.c \
getopt.h getopt.c getopt1.c \
Index: src/conflicts.c
--- src/conflicts.c Sun, 07 Apr 2002 20:00:35 +0200 akim
+++ src/conflicts.c Sat, 25 May 2002 16:55:11 +0200 akim
@@ -43,7 +43,7 @@
static inline void
log_resolution (state_t *state, int LAno, int token, const char *resolution)
{
- if (verbose_flag)
+ if (report_flag & report_states)
obstack_fgrow4 (&output_obstack,
_("\
Conflict in state %d between rule %d and token %s resolved as %s.\n"),
Index: src/getargs.c
--- src/getargs.c Sat, 16 Mar 2002 13:01:57 +0100 akim
+++ src/getargs.c Sat, 25 May 2002 17:04:06 +0200 akim
@@ -19,22 +19,21 @@
Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA. */
-#include <stdio.h>
-#include "getopt.h"
#include "system.h"
-#include "files.h"
+#include "getopt.h"
+#include "argmatch.h"
#include "complain.h"
#include "getargs.h"
-#include "xalloc.h"
#include "options.h"
+#include "files.h"
int debug_flag = 0;
int defines_flag = 0;
int locations_flag = 0;
int no_lines_flag = 0;
int no_parser_flag = 0;
+int report_flag = 0;
int token_table_flag = 0;
-int verbose_flag = 0;
int yacc_flag = 0; /* for -y */
int graph_flag = 0;
int trace_flag = 0;
@@ -44,6 +43,49 @@
extern char *program_name;
+/*----------------------.
+| --report's handling. |
+`----------------------*/
+
+static const char * const report_args[] =
+{
+ /* In a series of synonyms, present the most meaningful first, so
+ that argmatch_valid be more readable. */
+ "none",
+ "state", "states",
+ "itemset", "itemsets",
+ "lookahead", "lookaheads",
+ "all",
+ 0
+};
+
+static const int report_types[] =
+{
+ report_none,
+ report_states, report_states,
+ report_states | report_itemsets, report_states | report_itemsets,
+ report_states | report_lookaheads, report_states | report_lookaheads,
+ report_all
+};
+
+
+static void
+report_argmatch (char *args)
+{
+ char *arg = strtok (args, ",");
+ ARGMATCH_ASSERT (report_args, report_types);
+ do
+ {
+ int report = XARGMATCH ("--report", args,
+ report_args, report_types);
+ if (report == report_none)
+ report_flag = report_none;
+ else
+ report_flag |= report;
+ }
+ while ((args = strtok (NULL, ",")));
+}
+
/*---------------------------.
| Display the help message. |
`---------------------------*/
@@ -89,10 +131,18 @@ Parser:\n\
fputs (_("\
Output:\n\
-d, --defines also produce a header file\n\
- -v, --verbose also produce an explanation of the automaton\n\
+ -r, --report=THINGS also produce details on the automaton\n\
+ -v, --verbose same as `--report=state'\n\
-b, --file-prefix=PREFIX specify a PREFIX for output files\n\
-o, --output=FILE leave output to FILE\n\
-g, --graph also produce a VCG description of the automaton\n\
+\n\
+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 lookaheads to items\n\
+ `all' include all the above information\n\
+ `none' disable the report\n\
"), stream);
putc ('\n', stream);
@@ -170,7 +220,7 @@ Output:\n\
break;
case 'v':
- verbose_flag = 1;
+ report_flag |= report_states;
break;
case 'S':
@@ -196,10 +246,6 @@ Output:\n\
token_table_flag = 1;
break;
- case 'r':
- fatal (_("`%s' is no longer supported"), "--raw");
- break;
-
case 'n':
no_parser_flag = 1;
break;
@@ -218,6 +264,10 @@ Output:\n\
case 'p':
spec_name_prefix = optarg;
+ break;
+
+ case 'r':
+ report_argmatch (optarg);
break;
default:
Index: src/getargs.h
--- src/getargs.h Sat, 08 Dec 2001 17:14:16 +0100 akim
+++ src/getargs.h Sat, 25 May 2002 16:53:55 +0200 akim
@@ -1,5 +1,6 @@
/* Parse command line arguments for bison.
- Copyright 1984, 1986, 1989, 1992, 2000, 2001 Free Software Foundation, Inc.
+ Copyright 1984, 1986, 1989, 1992, 2000, 2001, 2002
+ Free Software Foundation, Inc.
This file is part of Bison, the GNU Compiler Compiler.
@@ -31,10 +32,21 @@
extern int no_lines_flag; /* for -l */
extern int no_parser_flag; /* for -n */
extern int token_table_flag; /* for -k */
-extern int verbose_flag; /* for -v */
extern int graph_flag; /* for -g */
extern int yacc_flag; /* for -y */
extern int trace_flag;
+
+/* --report. */
+enum
+ {
+ report_none = 0,
+ report_states = 1 << 0,
+ report_itemsets = 1 << 1,
+ report_lookaheads = 1 << 2,
+ report_all = ~0
+ };
+
+extern int report_flag;
void getargs PARAMS ((int argc, char *argv[]));
Index: src/main.c
--- src/main.c Mon, 06 May 2002 19:46:46 +0200 akim
+++ src/main.c Sat, 25 May 2002 16:57:59 +0200 akim
@@ -91,7 +91,7 @@
compute_output_file_names ();
/* Output the detailed report on the grammar. */
- if (verbose_flag)
+ if (report_flag)
print_results ();
/* Stop if there were errors, to avoid trashing previous output
Index: src/options.c
--- src/options.c Thu, 02 May 2002 19:30:29 +0200 akim
+++ src/options.c Mon, 20 May 2002 18:42:30 +0200 akim
@@ -29,7 +29,7 @@
#include "options.h"
/* Shorts options. */
-const char *shortopts = "yvegdhrltknVo:b:p:S:";
+const char *shortopts = "yvegdhr:ltknVo:b:p:S:";
/* A CLI option only.
Arguments is the policy: `no', `optional', `required'.
@@ -71,6 +71,8 @@
OPTN ("output", required, 0, 0, 'o')
OPTN ("output-file", required, 0, 0, 'o')
OPTN ("graph", optional, 0, 0, 'g')
+ OPTN ("report", required, 0, 0, 'r')
+ OPTN ("verbose", no, 0, 0, 'v')
/* Hidden. */
OPTN ("trace", no, &trace_flag, 0, 1)
@@ -111,7 +113,6 @@
/* Output. */
BOTH ("defines", optional, &defines_flag, tok_intopt, 'd')
- BOTH ("verbose", no, &verbose_flag, tok_intopt, 'v')
/* Operation modes. */
BOTH ("fixed-output-files", no, &yacc_flag, tok_intopt, 'y')
@@ -122,7 +123,7 @@
BOTH ("locations", no, &locations_flag, tok_intopt, 1)
BOTH ("no-lines", no, &no_lines_flag, tok_intopt, 'l')
BOTH ("no-parser", no, &no_parser_flag, tok_intopt, 'n')
- BOTH ("raw", no, 0, tok_obsolete, 'r')
+ BOTH ("raw", no, 0, tok_obsolete, 0)
BOTH ("skeleton", required, 0, tok_skel, 'S')
BOTH ("token-table", no, &token_table_flag, tok_intopt, 'k')
Index: src/print.c
--- src/print.c Sun, 05 May 2002 13:42:16 +0200 akim
+++ src/print.c Sat, 25 May 2002 17:12:02 +0200 akim
@@ -72,9 +72,8 @@
item_number_t *sitems = state->items;
int snritems = state->nitems;
- /* New experimental feature: if TRACE_FLAGS output all the items of
- a state, not only its kernel. */
- if (trace_flag)
+ /* Output all the items of a state, not only its kernel. */
+ if (report_flag & report_itemsets)
{
closure (sitems, snritems);
sitems = itemset;
@@ -105,8 +104,8 @@
for (/* Nothing */; *sp >= 0; ++sp)
fprintf (out, " %s", escape (symbols[*sp]->tag));
- /* Experimental feature: display the lookaheads. */
- if (trace_flag && state->nlookaheads)
+ /* Display the lookaheads? */
+ if (report_flag & report_lookaheads)
{
int j, k;
int nlookaheads = 0;
@@ -513,10 +512,9 @@
print_grammar (out);
- /* New experimental feature: output all the items of a state, not
- only its kernel. Requires to run closure, which need memory
- allocation/deallocation. */
- if (trace_flag)
+ /* If the whole state item sets, not only the kernels, are wanted,
+ `closure' will be run, which needs memory allocation/deallocation. */
+ if (report_flag & report_itemsets)
new_closure (nritems);
/* Storage for print_reductions. */
shiftset = bitset_create (ntokens, BITSET_FIXED);
@@ -525,7 +523,7 @@
print_state (out, states[i]);
bitset_free (shiftset);
bitset_free (lookaheadset);
- if (trace_flag)
+ if (report_flag & report_itemsets)
free_closure ();
xfclose (out);
Index: lib/argmatch.h
--- lib/argmatch.h Sat, 25 May 2002 17:19:30 +0200 akim
+++ lib/argmatch.h Mon, 20 May 2002 18:03:33 +0200 akim
@@ -0,0 +1,129 @@
+/* argmatch.h -- definitions and prototypes for argmatch.c
+ Copyright (C) 1990, 1998, 1999 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+/* Written by David MacKenzie <address@hidden>
+ Modified by Akim Demaille <address@hidden> */
+
+#ifndef ARGMATCH_H_
+# define ARGMATCH_H_ 1
+
+# if HAVE_CONFIG_H
+# include <config.h>
+# endif
+
+# include <sys/types.h>
+
+# ifndef PARAMS
+# if PROTOTYPES || (defined (__STDC__) && __STDC__)
+# define PARAMS(args) args
+# else
+# define PARAMS(args) ()
+# endif /* GCC. */
+# endif /* Not PARAMS. */
+
+/* Assert there are as many real arguments as there are values
+ (argument list ends with a NULL guard). There is no execution
+ cost, since it will be statically evalauted to `assert (0)' or
+ `assert (1)'. Unfortunately there is no -Wassert-0. */
+
+# undef ARRAY_CARDINALITY
+# define ARRAY_CARDINALITY(Array) (sizeof ((Array)) / sizeof (*(Array)))
+
+# define ARGMATCH_ASSERT(Arglist, Vallist) \
+ assert (ARRAY_CARDINALITY ((Arglist)) == ARRAY_CARDINALITY ((Vallist)) + 1)
+
+/* Return the index of the element of ARGLIST (NULL terminated) that
+ matches with ARG. If VALLIST is not NULL, then use it to resolve
+ false ambiguities (i.e., different matches of ARG but corresponding
+ to the same values in VALLIST). */
+
+int argmatch
+ PARAMS ((const char *arg, const char *const *arglist,
+ const char *vallist, size_t valsize));
+int argcasematch
+ PARAMS ((const char *arg, const char *const *arglist,
+ const char *vallist, size_t valsize));
+
+# define ARGMATCH(Arg, Arglist, Vallist) \
+ argmatch ((Arg), (Arglist), (const char *) (Vallist), sizeof (*(Vallist)))
+
+# define ARGCASEMATCH(Arg, Arglist, Vallist) \
+ argcasematch ((Arg), (Arglist), (const char *) (Vallist), sizeof
(*(Vallist)))
+
+/* xargmatch calls this function when it fails. This function should not
+ return. By default, this is a function that calls ARGMATCH_DIE which
+ in turn defaults to `exit (EXIT_FAILURE)'. */
+typedef void (*argmatch_exit_fn) PARAMS ((void));
+extern argmatch_exit_fn argmatch_die;
+
+/* Report on stderr why argmatch failed. Report correct values. */
+
+void argmatch_invalid
+ PARAMS ((const char *context, const char *value, int problem));
+
+/* Left for compatibility with the old name invalid_arg */
+
+# define invalid_arg(Context, Value, Problem) \
+ argmatch_invalid ((Context), (Value), (Problem))
+
+
+
+/* Report on stderr the list of possible arguments. */
+
+void argmatch_valid
+ PARAMS ((const char *const *arglist,
+ const char *vallist, size_t valsize));
+
+# define ARGMATCH_VALID(Arglist, Vallist) \
+ argmatch_valid (Arglist, (const char *) Vallist, sizeof (*(Vallist)))
+
+
+
+/* Same as argmatch, but upon failure, reports a explanation on the
+ failure, and exits using the function EXIT_FN. */
+
+int __xargmatch_internal
+ PARAMS ((const char *context,
+ const char *arg, const char *const *arglist,
+ const char *vallist, size_t valsize,
+ int case_sensitive, argmatch_exit_fn exit_fn));
+
+/* Programmer friendly interface to __xargmatch_internal. */
+
+# define XARGMATCH(Context, Arg, Arglist, Vallist) \
+ (Vallist [__xargmatch_internal ((Context), (Arg), (Arglist), \
+ (const char *) (Vallist), \
+ sizeof (*(Vallist)), \
+ 1, argmatch_die)])
+
+# define XARGCASEMATCH(Context, Arg, Arglist, Vallist) \
+ (Vallist [__xargmatch_internal ((Context), (Arg), (Arglist), \
+ (const char *) (Vallist), \
+ sizeof (*(Vallist)), \
+ 0, argmatch_die)])
+
+/* Convert a value into a corresponding argument. */
+
+const char *argmatch_to_argument
+ PARAMS ((char const *value, const char *const *arglist,
+ const char *vallist, size_t valsize));
+
+# define ARGMATCH_TO_ARGUMENT(Value, Arglist, Vallist) \
+ argmatch_to_argument ((char const *) &(Value), (Arglist), \
+ (const char *) (Vallist), sizeof (*(Vallist)))
+
+#endif /* ARGMATCH_H_ */
Index: lib/argmatch.c
--- lib/argmatch.c Sat, 25 May 2002 17:19:30 +0200 akim
+++ lib/argmatch.c Mon, 20 May 2002 17:48:27 +0200 akim
@@ -0,0 +1,306 @@
+/* argmatch.c -- find a match for a string in an array
+ Copyright (C) 1990, 1998, 1999 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+/* Written by David MacKenzie <address@hidden>
+ Modified by Akim Demaille <address@hidden> */
+
+#include "argmatch.h"
+
+#include <stdio.h>
+#ifdef STDC_HEADERS
+# include <string.h>
+#endif
+
+#if HAVE_LOCALE_H
+# include <locale.h>
+#endif
+
+#if ENABLE_NLS
+# include <libintl.h>
+# define _(Text) gettext (Text)
+#else
+# define _(Text) Text
+#endif
+
+#include "error.h"
+#include "quotearg.h"
+
+/* When reporting an invalid argument, show nonprinting characters
+ by using the quoting style ARGMATCH_QUOTING_STYLE. Do not use
+ literal_quoting_style. */
+#ifndef ARGMATCH_QUOTING_STYLE
+# define ARGMATCH_QUOTING_STYLE locale_quoting_style
+#endif
+
+/* The following test is to work around the gross typo in
+ systems like Sony NEWS-OS Release 4.0C, whereby EXIT_FAILURE
+ is defined to 0, not 1. */
+#if !EXIT_FAILURE
+# undef EXIT_FAILURE
+# define EXIT_FAILURE 1
+#endif
+
+/* Non failing version of argmatch call this function after failing. */
+#ifndef ARGMATCH_DIE
+# define ARGMATCH_DIE exit (EXIT_FAILURE)
+#endif
+
+#ifdef ARGMATCH_DIE_DECL
+ARGMATCH_DIE_DECL;
+#endif
+
+static void
+__argmatch_die (void)
+{
+ ARGMATCH_DIE;
+}
+
+/* Used by XARGMATCH and XARGCASEMATCH. See description in argmatch.h.
+ Default to __argmatch_die, but allow caller to change this at run-time. */
+argmatch_exit_fn argmatch_die = __argmatch_die;
+
+
+/* If ARG is an unambiguous match for an element of the
+ null-terminated array ARGLIST, return the index in ARGLIST
+ of the matched element, else -1 if it does not match any element
+ or -2 if it is ambiguous (is a prefix of more than one element).
+ If SENSITIVE, comparison is case sensitive.
+
+ If VALLIST is none null, use it to resolve ambiguities limited to
+ synonyms, i.e., for
+ "yes", "yop" -> 0
+ "no", "nope" -> 1
+ "y" is a valid argument, for `0', and "n" for `1'. */
+
+static int
+__argmatch_internal (const char *arg, const char *const *arglist,
+ const char *vallist, size_t valsize,
+ int case_sensitive)
+{
+ int i; /* Temporary index in ARGLIST. */
+ size_t arglen; /* Length of ARG. */
+ int matchind = -1; /* Index of first nonexact match. */
+ int ambiguous = 0; /* If nonzero, multiple nonexact match(es). */
+
+ arglen = strlen (arg);
+
+ /* Test all elements for either exact match or abbreviated matches. */
+ for (i = 0; arglist[i]; i++)
+ {
+ if (case_sensitive
+ ? !strncmp (arglist[i], arg, arglen)
+ : !strncasecmp (arglist[i], arg, arglen))
+ {
+ if (strlen (arglist[i]) == arglen)
+ /* Exact match found. */
+ return i;
+ else if (matchind == -1)
+ /* First nonexact match found. */
+ matchind = i;
+ else
+ {
+ /* Second nonexact match found. */
+ if (vallist == NULL
+ || memcmp (vallist + valsize * matchind,
+ vallist + valsize * i, valsize))
+ {
+ /* There is a real ambiguity, or we could not
+ disambiguate. */
+ ambiguous = 1;
+ }
+ }
+ }
+ }
+ if (ambiguous)
+ return -2;
+ else
+ return matchind;
+}
+
+/* argmatch - case sensitive version */
+int
+argmatch (const char *arg, const char *const *arglist,
+ const char *vallist, size_t valsize)
+{
+ return __argmatch_internal (arg, arglist, vallist, valsize, 1);
+}
+
+/* argcasematch - case insensitive version */
+int
+argcasematch (const char *arg, const char *const *arglist,
+ const char *vallist, size_t valsize)
+{
+ return __argmatch_internal (arg, arglist, vallist, valsize, 0);
+}
+
+/* Error reporting for argmatch.
+ CONTEXT is a description of the type of entity that was being matched.
+ VALUE is the invalid value that was given.
+ PROBLEM is the return value from argmatch. */
+
+void
+argmatch_invalid (const char *context, const char *value, int problem)
+{
+ char const *format = (problem == -1
+ ? _("invalid argument %s for `%s'")
+ : _("ambiguous argument %s for `%s'"));
+
+ error (0, 0, format, quotearg_style (ARGMATCH_QUOTING_STYLE, value),
context);
+}
+
+/* List the valid arguments for argmatch.
+ ARGLIST is the same as in argmatch.
+ VALLIST is a pointer to an array of values.
+ VALSIZE is the size of the elements of VALLIST */
+void
+argmatch_valid (const char *const *arglist,
+ const char *vallist, size_t valsize)
+{
+ int i;
+ const char *last_val = NULL;
+
+ /* We try to put synonyms on the same line. The assumption is that
+ synonyms follow each other */
+ fprintf (stderr, _("Valid arguments are:"));
+ for (i = 0; arglist[i]; i++)
+ if ((i == 0)
+ || memcmp (last_val, vallist + valsize * i, valsize))
+ {
+ fprintf (stderr, "\n - `%s'", arglist[i]);
+ last_val = vallist + valsize * i;
+ }
+ else
+ {
+ fprintf (stderr, ", `%s'", arglist[i]);
+ }
+ putc ('\n', stderr);
+}
+
+/* Never failing versions of the previous functions.
+
+ CONTEXT is the context for which argmatch is called (e.g.,
+ "--version-control", or "$VERSION_CONTROL" etc.). Upon failure,
+ calls the (supposed never to return) function EXIT_FN. */
+
+int
+__xargmatch_internal (const char *context,
+ const char *arg, const char *const *arglist,
+ const char *vallist, size_t valsize,
+ int case_sensitive,
+ argmatch_exit_fn exit_fn)
+{
+ int res = __argmatch_internal (arg, arglist,
+ vallist, valsize,
+ case_sensitive);
+ if (res >= 0)
+ /* Success. */
+ return res;
+
+ /* We failed. Explain why. */
+ argmatch_invalid (context, arg, res);
+ argmatch_valid (arglist, vallist, valsize);
+ (*exit_fn) ();
+
+ return -1; /* To please the compilers. */
+}
+
+/* Look for VALUE in VALLIST, an array of objects of size VALSIZE and
+ return the first corresponding argument in ARGLIST */
+const char *
+argmatch_to_argument (const char *value,
+ const char *const *arglist,
+ const char *vallist, size_t valsize)
+{
+ int i;
+
+ for (i = 0; arglist[i]; i++)
+ if (!memcmp (value, vallist + valsize * i, valsize))
+ return arglist[i];
+ return NULL;
+}
+
+#ifdef TEST
+/*
+ * Based on "getversion.c" by David MacKenzie <address@hidden>
+ */
+char *program_name;
+extern const char *getenv ();
+
+/* When to make backup files. */
+enum backup_type
+{
+ /* Never make backups. */
+ none,
+
+ /* Make simple backups of every file. */
+ simple,
+
+ /* Make numbered backups of files that already have numbered backups,
+ and simple backups of the others. */
+ numbered_existing,
+
+ /* Make numbered backups of every file. */
+ numbered
+};
+
+/* Two tables describing arguments (keys) and their corresponding
+ values */
+static const char *const backup_args[] =
+{
+ "no", "none", "off",
+ "simple", "never",
+ "existing", "nil",
+ "numbered", "t",
+ 0
+};
+
+static const enum backup_type backup_vals[] =
+{
+ none, none, none,
+ simple, simple,
+ numbered_existing, numbered_existing,
+ numbered, numbered
+};
+
+int
+main (int argc, const char *const *argv)
+{
+ const char *cp;
+ enum backup_type backup_type = none;
+
+ program_name = (char *) argv[0];
+
+ if (argc > 2)
+ {
+ fprintf (stderr, "Usage: %s [VERSION_CONTROL]\n", program_name);
+ exit (1);
+ }
+
+ if ((cp = getenv ("VERSION_CONTROL")))
+ backup_type = XARGCASEMATCH ("$VERSION_CONTROL", cp,
+ backup_args, backup_vals);
+
+ if (argc == 2)
+ backup_type = XARGCASEMATCH (program_name, argv[1],
+ backup_args, backup_vals);
+
+ printf ("The version control is `%s'\n",
+ ARGMATCH_TO_ARGUMENT (backup_type, backup_args, backup_vals));
+
+ return 0;
+}
+#endif