Next: , Previous: Enabling Traces, Up: Tracing


8.4.2 Enabling Debug Traces for mfcalc

The debugging information normally gives the token type of each token read, but not its semantic value. The %printer directive allows specify how semantic values are reported, see Printing Semantic Values. For backward compatibility, Yacc like C parsers may also use the YYPRINT (see The YYPRINT Macro), but its use is discouraged.

As a demonstration of %printer, consider the multi-function calculator, mfcalc (see Multi-function Calc). To enable run-time traces, and semantic value reports, insert the following directives in its prologue:

     /* 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", $$); } <double>;

The %define directive instructs Bison to generate run-time trace support. Then, activation of these traces is controlled at run-time by the 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 a description of that parser; this is the purpose of (admittedly ill-named) %verbose directive.

The set of %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., VAR or FNCT), or on the type tag: since <double> is the type for both NUM and exp, this printer will be used for them.

Here is a sample of the information provided by run-time traces. The traces are sent onto standard error.

     $ 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

This first batch shows a specific feature of this grammar: the first rule (which is in line 34 of mfcalc.y can be reduced without even having to look for the first token. The resulting left-hand symbol ($$) is a valueless (‘()’) input non terminal (nterm).

Then the parser calls the scanner.

     Reading a token: Next token is token FNCT (sin())
     Shifting token FNCT (sin())
     Entering state 6

That token (token) is a function (FNCT) whose value is ‘sin’ as formatted per our %printer specification: ‘sin()’. The parser stores (Shifting) that token, and others, until it can do something about it.

     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

The previous reduction demonstrates the %printer directive for <double>: both the token NUM and the resulting nonterminal exp have ‘1’ as value.

     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

The rule for the subtraction was just reduced. The parser is about to discover the end of the call to sin.

     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

Finally, the end-of-line allow the parser to complete the computation, and display its result.

     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' ()
     ⇒ 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

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.

     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 ()