Resolving Errors in a Yacc and Flex Compiler Project | Cannot generate AST from YACC Grammar

49 Views Asked by At

I want to display an AST for a given expression, and given below is my code for the same

Lex File


%{
    #include "y.tab.h"
    #include "ast.h"
%}

%option yylineno

digit [0-9]
int {digit}+
%%

"(" {return LPAREN;}
")" {return RPAREN;}
"+" {return PLUS;}
"-" {return MINUS;}
"*" {return MULT;}
"/" {return DIV;}
"%" {return MOD;}
"!" {return NOT;}
"&" {return AND;}
"|" {return OR;}
"^" {return XOR;}
{int} {
    yylval.number = atoi(yytext);
    return NUM;
}
"\n" {return TERM;}

%%

int yywrap() {
    return 1;
}

Yacc File

%{
    #include <stdio.h>
    #include <stdlib.h>
    #include <ctype.h>
    #include "lex.yy.c"
    void yyerror(const char *s);
    int yylex();
    int yywrap();
%}

%union{
    ASTNode* ast_node;
    int number;
}

%token LPAREN RPAREN PLUS MINUS
MULT DIV MOD NOT AND OR XOR TERM


%token <number> NUM

%type <ast_node> main
%type <ast_node> calc
%type <ast_node> expr

%left PLUS MINUS
%left MULT DIV MOD
%left AND OR XOR
%right UNARY_MINUS
%right NOT

%%

main: main calc
| calc
;

calc: expr TERM                 {
    $$ = $1;
    // print_ast($$,0); //print the ast
    // free_ast($$); //free the ast
    printf("\n");
}
;

expr: NUM                       {$<ast_node>$ = create_number_node($1);}
| MINUS expr %prec UNARY_MINUS  {$<ast_node>$ = create_unary_op_node('!', $2);}
| NOT expr %prec NOT            {$<ast_node>$ = create_unary_op_node('!', $2);}
| expr PLUS expr                {$<ast_node>$ = create_binary_op_node('+', $1, $3);}
| expr MINUS expr               {$<ast_node>$ = create_binary_op_node('-', $1, $3);}
| expr MULT expr                {$<ast_node>$ = create_binary_op_node('*', $1, $3);}
| expr DIV expr                 {$<ast_node>$ = create_binary_op_node('/', $1, $3);}
| expr MOD expr                 {$<ast_node>$ = create_binary_op_node('%', $1, $3);}
| expr AND expr                 {$<ast_node>$ = create_binary_op_node('&', $1, $3);}
| expr OR expr                  {$<ast_node>$ = create_binary_op_node('|', $1, $3);}
| expr XOR expr                 {$<ast_node>$ = create_binary_op_node('^', $1, $3);}
| LPAREN expr RPAREN            {$<ast_node>$ = $2;}
;

%%

int main(){
    yyparse();
}

void yyerror(const char *msg){
    fprintf(stderr, " [ line: %d ] %s at at token [ '%s' ] \n", yylineno, msg, yytext);
}

ast.h

#ifndef AST_H
#define AST_H

typedef struct ASTNode {
    char type;
    int value;
    struct ASTNode* left;
    struct ASTNode* right;
} ASTNode;

ASTNode* create_unary_op_node(char type, ASTNode* left);
ASTNode* create_binary_op_node(char type, ASTNode* left, ASTNode* right);
ASTNode* create_number_node(int value);
void print_ast(ASTNode* node, int depth);
void free_ast(ASTNode* node);

#endif

ast.c

#include <stdio.h>
#include <stdlib.h>
#include "ast.h"

ASTNode* create_number_node(int value) {
    ASTNode* node = (ASTNode*)malloc(sizeof(ASTNode));
    node->type = 'N';
    node->value = value;
    node->left = NULL;
    node->right = NULL;
    return node;
}

ASTNode* create_unary_op_node(char type, ASTNode* left) {
    ASTNode* node = (ASTNode*)malloc(sizeof(ASTNode));
    node->type = type;
    node->value = 0;
    node->left = left;
    node->right = NULL;
    return node;
}

ASTNode* create_binary_op_node(char type, ASTNode* left, ASTNode* right) {
    ASTNode* node = (ASTNode*)malloc(sizeof(ASTNode));
    node->type = type;
    node->value = 0;
    node->left = left;
    node->right = right;
    return node;
}

void print_ast(ASTNode* node, int depth) {
    if (node == NULL) {
        return;
    }

    // Indent based on the depth in the tree
    for (int i = 0; i < depth; i++) {
        printf("  ");
    }

    if (node != NULL) {
        printf("%c", node->type);
        if ( node->type == 'N') {
            printf("(%d)\n", node->value);
        } else {
            printf("\n");
        }
    } else {
        printf("NULL\n");
    }

    print_ast(node->left, depth + 1);
    print_ast(node->right, depth + 1);
}

void free_ast(ASTNode* node) {
    if (node == NULL) {
        return;
    }
    free_ast(node->left);
    free_ast(node->right);
    free(node);
}

The make returns the following error

flex -o bin/lex.yy.c src/bas.l
yacc -o bin/y.tab.c -d src/bas.y
cp src/ast.c bin
cp src/ast.h bin
gcc -o bin/bas bin/y.tab.c bin/ast.c -ll
In file included from src/bas.l:2,
                 from src/bas.y:5:
src/bas.y:12:5: error: unknown type name ‘ASTNode’
   12 |     ASTNode* ast_node;
      |     ^~~~~~~
src/bas.y: In function ‘yyparse’:
src/bas.y:46:71: warning: assignment to ‘int *’ from incompatible pointer type ‘ASTNode *’ {aka ‘struct ASTNode *’} [-Wincompatible-pointer-types]
   46 | expr: NUM         {$<ast_node>$ = create_number_node($1);}
      |                                                                       ^
src/bas.y:47:88: warning: passing argument 2 of ‘create_unary_op_node’ from incompatible pointer type [-Wincompatible-pointer-types]
   47 | | MINUS expr %prec UNARY_MINUS {$<ast_node>$ = create_unary_op_node('!', $2);}
      |                                                                                        ^         
      |                                                                                        |
      |                                                                                        int *
In file included from src/bas.l:3,
                 from src/bas.y:5:
bin/ast.h:11:10: note: expected ‘ASTNode *’ {aka ‘struct ASTNode *’} but argument is of type ‘int *’
   11 | ASTNode* create_unary_op_node(char type, ASTNode* left);
      |          ^~~~~~~~~~~~~~~~~~~~
src/bas.y:47:51: warning: assignment to ‘int *’ from incompatible pointer type ‘ASTNode *’ {aka ‘struct ASTNode *’} [-Wincompatible-pointer-types]
   47 | | MINUS expr %prec UNARY_MINUS {$<ast_node>$ = create_unary_op_node('!', $2);}
      |                                                   ^
src/bas.y:48:96: warning: passing argument 2 of ‘create_unary_op_node’ from incompatible pointer type [-Wincompatible-pointer-types]
   48 | | NOT expr %prec NOT   {$<ast_node>$ = create_unary_op_node('!', $2);}
      |                                                                                                ^         
      |                                                                                                |
      |                                                                                                int *
In file included from src/bas.l:3,
                 from src/bas.y:5:
bin/ast.h:11:10: note: expected ‘ASTNode *’ {aka ‘struct ASTNode *’} but argument is of type ‘int *’
   11 | ASTNode* create_unary_op_node(char type, ASTNode* left);
  • I have checked the data types on the ast files
  • I have checked with other examples and tried enforcing type with the $<ast_node>$ gimmick
  • I don't want typecast pointers in this one as I believe there has to be a straight way to do this and there's something I am doing wrong.
1

There are 1 best solutions below

6
Kaushik Dey On

replacing ASTNode* with struct ASTNode* in the %union declaration somehow made it work well, but now I am confused as why did the typedef not work?