3
votes

I'm new to flex and bison so bear with me. I'm trying to use yylloc in yyerror to print out where the error occurs along with the filename. I know that this requires me to redefine YYLTPYE to include a char* filename that I can use to keep track of the filename. According to the Flex and Bison book I have, it recommends that I use the YY_USER_ACTION macro to initialize the YYLTYPE in the .l file, so I included the following in it,

#define YY_USER_ACTION yylloc.filename = filename; yylloc.hel = 0;  \
        yylloc.first_line = yylloc.last_line = yylineno;            \
        yylloc.first_column = yycolumn; yylloc.last_column = yycolumn+yyleng-1; \
        yycolumn += yyleng;

but when I try to compile the project, I get the error that yylloc is undeclared.

I've tried the solution offered by Chris Dodd in this question, but it hasn't helped me to resolve the issue. Any and all help in resolving this error is much apprecaited.

Here's the full code in .l:

%option noyywrap nodefault yylineno case-insensitive
%{
    #include "need.h"
    #include "numbers.tab.h"

    int yycolumn = 1;

    #define YY_USER_ACTION yylloc.filename = filename; yylloc.hel = 0;  \
        yylloc.first_line = yylloc.last_line = yylineno;            \
        yylloc.first_column = yycolumn; yylloc.last_column = yycolumn+yyleng-1; \
        yycolumn += yyleng;

%}

Integers    [-]?(0|[1-9][0-9]*)
Float       [.][0-9]+
Exp         [eE][-]?(0|[1-9][0-9]*)
Octal       [-]?(00|0[1-7][0-7]*)
Hexa        [-]?(0[xX][0-9A-F]+)
tomsNotNumbers [^ \t\n\v\f\r]+

%%

{Integers}{Float}?{Exp}?    {
                                printf("%s is a number.\n", yytext);
                                possibleNumbers++;  // increment by 1 as an input was given -M
                                actualNumbers++;    // increment by 1 as an input did match our pattern -M
                            }

{Octal} {
            printf("%s is a number.\n", yytext);
            possibleNumbers++;  // increment by 1 as an input was given -M
            actualNumbers++;    // increment by 1 as an input did match our pattern -M
        }

{Hexa}  {
            printf("%s is a number.\n", yytext);
            possibleNumbers++;  // increment by 1 as an input was given -M
            actualNumbers++;    // increment by 1 as an input did match our pattern -M
        }

{tomsNotNumbers}    {
                    printf("%s is not a number.\n", yytext);
                    yyerror(warning, "This isn't a number.");
                    possibleNumbers++;  // increment by 1 as an input was given -M
                    failedNumbers++;    // increment by 1 as the input has failed to match our patterns -M
                }

[\n]    /*Do nothing for newline*/

.   /*Do nothing for anything else*/

%%

.y is just empty for now, only has an include for need.h and one for .tab.h

The need.h:

#include <stdlib.h>
#include <stdarg.h>
#include <string.h>

int possibleNumbers = 0;
int actualNumbers = 0;
int failedNumbers = 0;

typedef struct YYLTYPE
{
    int first_line;
    int first_column;
    int last_line;
    int last_column;
    char *filename; /* use to keep track of which file we're currently in */
    int hel;    /* no errors = 0, warning = 1, error = 2, fatal = 3 */
} YYLTYPE;

char *name; /*using for test purposes*/

# define YYLTYPE_IS_DECLARED 1

# define YYLLOC_DEFAULT(Current, Rhs, N)                                                    \
    do                                                                                      \
        if (N)                                                                              \
        {                                                                                   \
            (Current).first_line = YYRHSLOC (Rhs, 1).first_line;                            \
            (Current).first_column = YYRHSLOC (Rhs, 1).first_column;                        \
            (Current).last_line = YYRHSLOC (Rhs, N).last_line;                              \
            (Current).last_column = YYRHSLOC (Rhs, N).last_column;                          \
            (Current).filename = YYRHSLOC (Rhs, 1).filename;                                \
            (Current).hel   = YYRHSLOC (Rhs, 1).hel;                                        \
        }                                                                                   \
        else                                                                                \
        { /* empty RHS */                                                                   \
            (Current).first_line = (Current).last_line = YYRHSLOC (Rhs, 0).last_line;       \
            (Current).first_column = (Current).last_column = YYRHSLOC (Rhs, 0).last_column; \
            (Current).filename  = NULL;                                                     \
            (Current).hel = 0;                                                              \
        }                                                                                   \
    while (0)

typedef enum errorSeverity
{
    warning = 1, error, fatal
} errorLevel;

void yyerror(errorLevel errlvl, char *s, ...)
{
    va_list ap;
    va_start(ap, s);
    char *errLvls[3] = {"Warning", "Error", "Fatal"};  

    fprintf(stderr, "%s: %s: , %n", name, errLvls[errlvl - 1], yylloc.first_line);
    vfprintf(stderr, s, ap);
    fprintf(stderr, "\n");
}

main(int argc, char **argv)
{
    printf("argv[0] = %s, argv[1] = %s.\n", argv[0], argv[1]);
    if(argc > 1)
    {
        if((yyin = fopen(argv[1], "r")) == NULL)
        {
            perror(argv[1]);
            exit(1);
        }
        name = argv[1];
    } else
        name = "(stdin)";

    printf("Filename1: %s", name);
    yylex();
    printf("Filename2: %s", name);
    // print out the report. -M
    printf("Out of %d possible numbers, there were %d numbers, and %d not numbers.\n", possibleNumbers, actualNumbers, failedNumbers);
}
1

1 Answers

3
votes

Since yylloc is normally defined in the bison-generated parser, not having a bison input file is going to be a bit of a nuisance.

Bison will define yylloc in the generated parser, and place a declaration in the generated header file, if:

  1. You include the directive %locations in the bison prologue, or

  2. You reference a location (@n for some n) in any bison action.

It is generally preferred to add the directive in case there is no explicit reference to a location in any rule.

As Chris Dodd says in the linked question, it is important to include the definition of YYLTYPE before #includeing the bison-generated header file. Alternatively, you could insert the definition of the structure, or an appropriate #include, directly in the bison prologue in a %code requires section. %code requires sections are copied to the generated header, so that will obviate the need to worry about the definition in the flex file.


By the way, I think you meant to use YY_USER_INIT to initialize yylloc. The expansion of YY_USER_INIT is executed only once, before the flex scanner's own initialization. The expansion of YY_USER_ACTION is executed before every scanner action (including empty actions), and is likely to be of use to update the yylloc structure with the current token.