0

I was trying a simple program to create an abstract syntax tree using lex and yacc.

My yacc_file.y is

%{

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

  typedef struct node
  {
    struct node *left;
    struct node *right;
    char *token;
  } node;

  node *mknode(node *left, node *right, char *token);
  void printtree(node *tree);

#define YYSTYPE struct node *

%}

%start lines

%token  NUMBER
%token  PLUS    MINUS   TIMES
%token  LEFT_PARENTHESIS    RIGHT_PARENTHESIS
%token  END

%left   PLUS    MINUS
%left   TIMES

%%

lines:  /* empty */
        | lines line /* do nothing */

line:   exp END     { printtree($1); printf("\n");}
    ;

exp    : term             {$$ = $1;}
        | exp PLUS term     {$$ = mknode($1, $3, "+");}
        | exp MINUS term    {$$ = mknode($1, $3, "-");}
        ;

term   : factor           {$$ = $1;}
        | term TIMES factor  {$$ = mknode($1, $3, "*");}
        ;

factor : NUMBER           {$$ = mknode(0,0,(char *)yylval);}
        | LEFT_PARENTHESIS exp RIGHT_PARENTHESIS {$$ = $2;}
        ;
%%

int main (void) {return yyparse ( );}

node *mknode(node *left, node *right, char *token)
{
  /* malloc the node */
  node *newnode = (node *)malloc(sizeof(node));
  char *newstr = (char *)malloc(strlen(token)+1);
  strcpy(newstr, token);
  newnode->left = left;
  newnode->right = right;
  newnode->token = newstr;
  return(newnode);
}

void printtree(node *tree)
{
  int i;


  if (tree->left || tree->right)
    printf("(");

  printf(" %s ", tree->token);

  if (tree->left)
    printtree(tree->left);
  if (tree->right)
    printtree(tree->right);

  if (tree->left || tree->right)
    printf(")");
}

int yyerror (char *s) {fprintf (stderr, "%s\n", s);}

My lex_file.l file is

%{
#include "yacc_file.tab.h"
%}


%%

[0-9]+                {yylval = (int)yytext; return NUMBER;} 
                       /* cast pointer to int for compiler warning */
[ \t\n]               ;
"+"      return(PLUS);
"-"      return(MINUS);
"*"      return(TIMES);
"("      return(LEFT_PARENTHESIS);
")"      return(RIGHT_PARENTHESIS);
";"      return(END);


%%

int yywrap (void) {return 1;}

To run, I have done the following

yacc -d yacc_file.y
lex lex_file.y
cc y.tab.c lex.yy.c -o a.exe

I got the following error

lexfile.l: In function 'yylex':
lex_file.l:10:2: error: 'yylval' undeclared(first used in this function)
[0-9]+                  {yylval=(int)yytext; return NUMBER;}

I have searched on google and %union seems to solve the problem. But I am not sure how to use it.

Apoorva Somani
  • 515
  • 1
  • 6
  • 23

1 Answers1

3

The command

yacc -d yacc_file.y

produces a header file called y.tab.h and a C file called y.tab.c. That's the yacc-compatible default naming, and it does not agree with your flex file, which is expecting the header to be called yacc_file.tab.h.

You could just change the #include statement in your flex file, but that wouldn't be compatible with the build system at your college. So I suggest you change to the command bison -d yacc_file.y instead of your yacc command. That will produce a header file called yacc_file.tab.h and a C file called yacc_file.tab.c. (Of course, you will then have to change the cc command to compile yacc_file.tab.c instead of y.tab.c.)

Presumably there is some incorrect yacc_file.tab.h on your machine, which doesn't include a declaration of yylval. Hence the compilation error.

To avoid confusing yourself further, when you fix your build procedure I'd recommend deleting all the intermediate files -- y.tab.h and y.tab.c as well as yacc_file.tab.c and yacc_file.tab.h, and lex.yy.c. Then you can do a clean build without having to worry about picking up some outdated intermediate file.


Also, in yacc_file.y, you #define YYSTYPE as struct node *. That's fine, but the #define will not be copied into the generated header file; in the header file, YYSTYPE will be #defined as int if there is no other #define before the header file is #included.

Moreover, in lex_file.l you use yylval as though it were an int (yylval = (int)yytext;) but I think that statement does not do what you think it does. What it does is reinterpret the address of yytext as an integer. That's legal but meaningless. What you wanted to do, I think, is to convert the string in yytext as an integer. To do that, you need to use strtod or some similar function from the standard C library.

Regardless, it is vital that the scanner and the parser agree on the type of yylval. Otherwise, things will go desperately wrong.

As you mention, it is possible to use a %union declaration to declare YYSTYPE as a union type. You should make sure you understand C union types, and also read the bison manual section on semantics..

rici
  • 234,347
  • 28
  • 237
  • 341
  • 1
    A better answer would have been to simply point to correcting the #include – Thomas Dickey Sep 07 '15 at 12:56
  • @ThomasDickey: OK, I edited the answer to make it clearer that changing the include statement is a possible fix, and why I think it might not be the best. – rici Sep 07 '15 at 19:59
  • The behavior in bison is not "new", but also rather old, and referring to standard behavior as old-fashioned is a problem with the answer. – Thomas Dickey Sep 07 '15 at 20:04
  • @thomas: Ok, removed the word in question. I don't see any use of the word "new" in my answer. – rici Sep 07 '15 at 20:09
  • @ThomasDickey: By the way, the "standard" (Posix) describes the fixed intermediate filenames as "a known deficiency in historical implementations", which could be seen as somewhat less polite than my abbreviated description. :) (fifth paragraph in http://pubs.opengroup.org/onlinepubs/9699919799/utilities/yacc.html#tag_20_159_18) – rici Sep 07 '15 at 20:42
  • The current answer is improved. If I had answered it, I might have pointed out that some `bison` packagers make a symbolic link to `yacc` (rather than provide a shell script with the `-y` option), which likely is the source of OP's confusion. Also, it is likely that on a home-machine, OP is using byacc, which also provides renamed files -- likewise implemented quite a while ago. – Thomas Dickey Sep 07 '15 at 21:53