This is a first stab at documenting %printer. I tend to think this should be demonstrated in the sections about mfcalc, as it is good practice, but maybe I should move all this in the "Debugging Your Parser" chapter. Even if I still rely on mfcalc to demonstrate the feature.
I still have to document %printer more formally, as %destructor is. But then again, I'm not sure where. Should it be next to %destructor, in "Bison Declarations", or be where YYPRINT used to be documented, i.e., the "Debugging Your Parser" chapter. I like the first option, because after all that section (Bison Declarations) is more or less structured as a genuine file would be, but on the other hand, documenting into "Understanding" groups related things together. This is for master, but if easy to back port, I will include it in maint. And then I think I am done with maint, so we could start the release process for 2.5.1. From e4dcd86fc36dac8e77eedb05f33e9a997e11abfe Mon Sep 17 00:00:00 2001 From: Akim Demaille <[email protected]> Date: Thu, 15 Mar 2012 16:16:26 +0100 Subject: [PATCH 6/6] doc: mfcalc: demonstrate %printer. * doc/bison.texinfo (Mfcalc Traces): New. --- doc/bison.texinfo | 185 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 177 insertions(+), 8 deletions(-) diff --git a/doc/bison.texinfo b/doc/bison.texinfo index 3f4687d..91fc358 100644 --- a/doc/bison.texinfo +++ b/doc/bison.texinfo @@ -176,6 +176,7 @@ Location Tracking Calculator: @code{ltcalc} Multi-Function Calculator: @code{mfcalc} * Mfcalc Declarations:: Bison declarations for multi-function calculator. +* Mfcalc Traces:: Run-time debug traces. * Mfcalc Rules:: Grammar rules for the calculator. * Mfcalc Symbol Table:: Symbol table management subroutines. * Mfcalc Lexer:: The lexical analyzer. @@ -2351,6 +2352,7 @@ Note that multiple assignment and nested function calls are permitted. @menu * Mfcalc Declarations:: Bison declarations for multi-function calculator. +* Mfcalc Traces:: Run-time debug traces. * Mfcalc Rules:: Grammar rules for the calculator. * Mfcalc Symbol Table:: Symbol table management subroutines. * Mfcalc Lexer:: The lexical analyzer. @@ -2373,6 +2375,7 @@ Here are the C and Bison declarations for the multi-function calculator. void yyerror (char const *); %@} @end group + @group %union @{ double val; /* For returning numbers. */ @@ -2380,7 +2383,7 @@ Here are the C and Bison declarations for the multi-function calculator. @} @end group %token <val> NUM /* Simple double precision number. */ -%token <tptr> VAR FNCT /* Variable and Function. */ +%token <tptr> VAR FNCT /* Variable and function. */ %type <val> exp @group @@ -2390,7 +2393,6 @@ Here are the C and Bison declarations for the multi-function calculator. %precedence NEG /* negation--unary minus */ %right '^' /* exponentiation */ @end group -%% /* The grammar follows. */ @end example The above grammar introduces only two new features of the Bison language. @@ -2415,6 +2417,166 @@ normally declared implicitly by the rules that define them. But @code{exp} must be declared explicitly so we can specify its value type. @xref{Type Decl, ,Nonterminal Symbols}. +@node Mfcalc Traces +@subsection Enabling Debug Traces for @code{mfcalc} + +In addition, in order to support run-time traces, the prologue is extend +with: +@comment file: mfcalc.y +@example +/* Generate the parser description file. */ +%verbose +/* Enable run-time traces (yydebug). */ +%define parse.trace + +/* Formatting semantic values. */ +%printer @{ fprintf (yyoutput, "%s", $$->name); @} VAR; +%printer @{ fprintf (yyoutput, "%s()", $$->name); @} FNCT; +%printer @{ fprintf (yyoutput, "%g", $$); @} <val>; +@end example + +The @code{%define} directive will instruct Bison to generate run-time trace +support. Then, activation of these traces is controlled at run-time by the +@code{yydebug} variable, which is disabled by default. Because these traces +will refer to the ``states'' of the parser, it is helpful to ask for the +creation of that parser; this is the purpose of (admittedly ill-named) +@code{%verbose} directive. + +The set of @code{%printer} directives demonstrates how to format the +semantic value in the traces. Note that the specification can be done +either on the symbol type (e.g., @code{VAR} or @code{FNCT}), or on the type +tag: since @code{<val>} is the type for both @code{NUM} and @code{exp}, this +printer will be used for them. + +Anticipating the completion of the sources of this example, here is a sample +of the information provided by run-time traces. The function @code{main} is +modified so that option @option{-p} activates the generated tracing code. +The traces are sent onto the standard error. + +@example +$ @kbd{echo 'sin(1-1)' | ./mfcalc -p} +Starting parse +Entering state 0 +Reducing stack by rule 1 (line 34): +-> $$ = nterm input () +Stack now 0 +Entering state 1 +@end example + +@noindent +This first batch shows a specific feature of this grammar: the first rule +(which is in line 34 of @file{mfcalc.y} can be reduced without even having +to look for the first token. The resulting left-hand symbol (@code{$$}) is +a valueless (@samp{()}) @code{input} non terminal (@code{nterm}). + +Then the parser calls the scanner. +@example +Reading a token: Next token is token FNCT (sin()) +Shifting token FNCT (sin()) +Entering state 6 +@end example + +@noindent +That token (@code{token}) is a function (@code{FNCT}) whose value is +@samp{sin} as formatted per our @code{%printer} specification: @samp{sin()}. +The parser stores (@code{Shifting}) that token, and others, until it can do +something about it. + +@example +Reading a token: Next token is token '(' () +Shifting token '(' () +Entering state 14 +Reading a token: Next token is token NUM (1.000000) +Shifting token NUM (1.000000) +Entering state 4 +Reducing stack by rule 6 (line 44): + $1 = token NUM (1.000000) +-> $$ = nterm exp (1.000000) +Stack now 0 1 6 14 +Entering state 24 +@end example + +@noindent +The previous reduction demonstrates the @code{%printer} directive for +@code{<val>}: both the token @code{NUM} and the resulting non-terminal +@code{exp} have @samp{1} as value. + +@example +Reading a token: Next token is token '-' () +Shifting token '-' () +Entering state 17 +Reading a token: Next token is token NUM (1.000000) +Shifting token NUM (1.000000) +Entering state 4 +Reducing stack by rule 6 (line 44): + $1 = token NUM (1.000000) +-> $$ = nterm exp (1.000000) +Stack now 0 1 6 14 24 17 +Entering state 26 +Reading a token: Next token is token ')' () +Reducing stack by rule 11 (line 49): + $1 = nterm exp (1.000000) + $2 = token '-' () + $3 = nterm exp (1.000000) +-> $$ = nterm exp (0.000000) +Stack now 0 1 6 14 +Entering state 24 +@end example + +@noindent +The rule for the subtraction was just reduced. The parser is about to +discover the end of the call to @code{sin}. + +@example +Next token is token ')' () +Shifting token ')' () +Entering state 31 +Reducing stack by rule 9 (line 47): + $1 = token FNCT (sin()) + $2 = token '(' () + $3 = nterm exp (0.000000) + $4 = token ')' () +-> $$ = nterm exp (0.000000) +Stack now 0 1 +Entering state 11 +@end example + +@noindent +Finally, the end-of-line allow the parser to complete the computation, and +display its result. + +@example +Reading a token: Next token is token '\n' () +Shifting token '\n' () +Entering state 22 +Reducing stack by rule 4 (line 40): + $1 = nterm exp (0.000000) + $2 = token '\n' () +@result{} 0 +-> $$ = nterm line () +Stack now 0 1 +Entering state 10 +Reducing stack by rule 2 (line 35): + $1 = nterm input () + $2 = nterm line () +-> $$ = nterm input () +Stack now 0 +Entering state 1 +@end example + +The parser has returned into state 1, in which it is waiting for the next +expression to evaluate, or for the end-of-file token, which causes the +completion of the parsing. + +@example +Reading a token: Now at end of input. +Shifting token $end () +Entering state 2 +Stack now 0 1 2 +Cleanup: popping token $end () +Cleanup: popping nterm input () +@end example + @node Mfcalc Rules @subsection Grammar Rules for @code{mfcalc} @@ -2424,6 +2586,7 @@ those which mention @code{VAR} or @code{FNCT}, are new. @comment file: mfcalc.y @example +%% /* The grammar follows. */ @group input: /* empty */ | input line @@ -2696,7 +2859,8 @@ yylex (void) @subsection The @code{mfcalc} Main The error reporting function is unchanged, and the new version of -@code{main} includes a call to @code{init_table}: +@code{main} includes a call to @code{init_table} and sets the @code{yydebug} +on user demand: @comment file: mfcalc.y @smallexample @@ -2714,6 +2878,11 @@ yyerror (char const *s) int main (int argc, char const* argv[]) @{ + int i; + /* Enable parse traces on option -p. */ + for (i = 1; i < argc; ++i) + if (!strcmp(argv[i], "-p")) + yydebug = 1; init_table (); return yyparse (); @} @@ -10052,7 +10221,7 @@ void calcxx_driver::scan_begin () @{ yy_flex_debug = trace_scanning; - if (file == "-") + if (file.empty () || file == "-") yyin = stdin; else if (!(yyin = fopen (file.c_str (), "r"))) @{ @@ -10087,12 +10256,12 @@ main (int argc, char *argv[]) @{ int res = 0; calcxx_driver driver; - for (++argv; argv[0]; ++argv) - if (*argv == std::string ("-p")) + for (int i = 1; i < argc; ++i) + if (argv[i] == std::string ("-p")) driver.trace_parsing = true; - else if (*argv == std::string ("-s")) + else if (argv[i] == std::string ("-s")) driver.trace_scanning = true; - else if (!driver.parse (*argv)) + else if (!driver.parse (argv[i])) std::cout << driver.result << std::endl; else res = 1; -- 1.7.9.2
