Ошибка Bison / Yacc: поле имеет неполный тип - PullRequest
0 голосов
/ 04 ноября 2018

Я пытаюсь создать компилятор Cminus и специально пытаюсь создать для него AST.

В моем файле step3.fl

%option noyywrap

%{
    /* Definitions and statements */
    #include "step3.tab.h"
    //extern YYSTYPE yylval;
    YYSTYPE yylval;
%}

ID      {letter}{letter}*
NUM     {digit}{digit}*
letter  [a-zA-Z]
digit   [0-9]

%option noinput nounput
%option yylineno
%option noyywrap

%%
"if"                                { return T_IF;      }
"else"                              { return T_ELSE;    }
"int"                               { return T_INT;     }
"return"                            { return T_RETURN;  }
"void"                              { return T_VOID;    }
"while"                             { return T_WHILE;   }
"+"                                 { return yytext[0]; }
"-"                                 { return yytext[0]; }
"*"                                 { return yytext[0]; }
"/"                                 { return yytext[0]; }
">"                                 { return T_GREAT;   }
">="                                { return T_GREATEQ; }
"<"                                 { return T_SMALL;   }
"<="                                { return T_SMALLEQ; }
"=="                                { return T_COMPARE; }
"!="                                { return T_NOTEQ;   }
"="                                 { return yytext[0]; }
";"                                 { return yytext[0]; }
","                                 { return yytext[0]; }
"("                                 { return yytext[0]; }
")"                                 { return yytext[0]; }
"["                                 { return yytext[0]; }
"]"                                 { return yytext[0]; }
"{"                                 { return yytext[0]; }
"}"                                 { return yytext[0]; }

{ID}        { yylval.string=strdup(yytext); return T_ID;    }
{NUM}       { yylval.value=atoi((const char *)yytext); return T_NUM;    }
%%

В моем файле step3.y:

%{
    #include <stdio.h>
    #include <ctype.h>
    #include <stdlib.h>
    #include "ast.h"
    #include "symtable.h"

    static int level=0;
    static int offset=0;
    static int goffset=0;
    static int maxoffset=0;

    extern int yylex();
    extern int yyparse();
    extern char *yytext;
    extern FILE *yyin;
    void yyerror(const char* s);
    extern int yylineno;
%}
%start program

%union {
    int value;
    char *string;
    struct ASTnode * node;
    enum OPERATORS op;
}

%right "then" T_ELSE

%token  T_IF T_ELSE T_INT T_RETURN T_VOID T_WHILE 
        T_GREAT T_GREATEQ T_SMALL T_SMALLEQ T_NOTEQ T_COMPARE 

%token <value> T_NUM
%token <string> T_ID
%type <node> declaration-list declaration var-declaration fun-declaration params
%type <node> param-list param compound-stmt local-declarations statement-list statement expression-stmt
%type <node> selection-stmt iteration-stmt return-stmt expression
%type <node> var simple-expression additive-expression term factor
%type <node> call args arg-list
%type <op> relop addop mulop type-specifier

%%

program: 
                    declaration-list    {prog=$1;}  
                    ;

declaration-list: 
                    declaration-list declaration { $1->left = $2; $$ = $1; }    
                    | declaration   { $$ = $1; }                    
                    ;

declaration: 
                    var-declaration     { $$ = $1; }            
                    | fun-declaration   { $$ = $1; }            
                    ;

var-declaration: 
                    type-specifier T_ID';'
                    {
                        /* search symbol table for ID
                        insert if not found, then add the pointer from
                        insertion into the  ASTnode to have reference
                        to the symbol table entry */
                        if(Search($2,level,0))
                        {
                            fprintf(stderr,"\n\tThe name %s exists at level %d ",$2,level);
                            fprintf(stderr,"already in the symbol table\n");
                            fprintf(stderr,"\tDuplicate can.t be inserted(found in search)");
                            yyerror(" ");
                            exit(1);
                        }

                        $$=ASTCreateNode(VARDEC);
                        $$->name=$2;
                        /* we use the op to determine its type while printing*/
                        $$->op=$1;
                        $$->symbol=Insert($2,$1,0,level,1,offset,NULL);
                        $$->isType=$1;
                        offset += 1;
                        if(offset > maxoffset)
                            maxoffset = offset;
                    }
                    | type-specifier T_ID'['T_NUM']' ';'
                    {   /* search for symbol, if we find it error*/
                        if(Search($2,level,0))
                        {
                            fprintf(stderr,"\n\tThe name %s exists at level %d ",$2,level);
                            fprintf(stderr,"already in the symbol table\n");
                            fprintf(stderr,"\tDuplicate can.t be inserted(found in search)");
                            yyerror(" ");
                            exit(1);
                        }

                        $$=ASTCreateNode(VARDEC);
                        $$->name=$2;
                        /* we use the op to determine its type while printing*/
                        $$->op=$1;
                        /* value links to the NUM nod to store the dimension*/
                        $$->value=$4;
                        $$->symbol=Insert($2,$1,2,level,$4,offset,NULL);
                        $$->isType=$1;
                        offset += $4;
                        if (offset>maxoffset)
                            maxoffset = offset;
                    }
                    ;

type-specifier:     T_INT       { $$ = INTDEC; }
                    | T_VOID    { $$ = VOIDDEC; }   
                    ;

fun-declaration: 
                    type-specifier T_ID '('
                    {   /*name already used, cant make new dec*/
                        if(Search($2,level,0))
                        {
                            yyerror($2);
                            yyerror("Name already used ");
                            exit(1);
                        }
                        Insert($2,$1,1,level,1,0,NULL);
                        goffset=offset;
                        offset=2;
                        if(offset>maxoffset)
                            maxoffset = offset;
                    }
                    params
                    {   /*need the formal params to compare later */
                        (Search($2,0,0))->fparms = $5;
                    }
                    ')' compound-stmt
                     {
                        $$=ASTCreateNode(FUNCTIONDEC);
                        $$->name=$2;
                        /* we use the op to determine its type while printing*/
                        $$->op=$1;
                        /* s1 links to the params which can be void
                            or a paramList */
                        $$->s1=$5;
                        /* right links to the compund statement,
                            called a BLOCK in the enumerated type */
                        $$->right=$8;
                        /*get the symbtab entry we made earlier*/
                        $$->symbol=Search($2,0,0);
                        /*Remove symbols put in, in the function call*/
                        offset -=Delete(1);
                        level = 0;
                        $$->value=maxoffset;
                        //we change this in the symbol table because it is not used
                        //anywhere else for functions. We have access to this
                        //in calls, so we can use it to determine where the
                        //stack pointer is going to have to be
                        $$->symbol->mysize = maxoffset;
                        /*change the offset back to the global offset*/
                        offset=goffset;
                        maxoffset=0;
                    }
                    ;

params:             param-list
                    {   /* params found */
                        $$ = $1;
                    }   
                    | T_VOID
                    {   /* no params */
                        $$ = NULL;

                    }
                    ;

param-list: 
                    param-list',' param 
                    {   /* attach the param to the list */
                        $1->left=$3;
                        $$ = $1;
                    }
                    | param                 { $$ = $1; }                
                    ;

param: 
                    type-specifier T_ID
                     {   /*dont want overlap between params and vars*/
                        if(Search($2,level,0))
                        {
                           yyerror($2);
                           yyerror("\tDuplicate can.t be inserted(found in search)");
                           exit(1);
                        }
                        $$ = ASTCreateNode(PARAM);
                        $$->name=$2;
                        /* we use the op to determine its type while printing*/
                        $$->op=$1;
                        /* if value is 0 it is not an array, used for printing */
                        $$->value=0;
                        /*inherit the type*/
                        $$->isType=$1;
                        $$->symbol=Insert($2,$1,0,level+1,1,offset,NULL);
                        offset+=1;
                        if(offset>maxoffset)
                            maxoffset = offset;
                    }
                    | type-specifier T_ID'['']'
                    {
                        if(Search($2,level,0))
                        {
                           yyerror($2);
                           yyerror("\tDuplicate can.t be inserted(found in search)");
                           exit(1);
                        }
                        $$ = ASTCreateNode(PARAM);
                        $$->name=$2;
                        /* we use the op to determine its type while printing*/
                        $$->op=$1;
                        /* there was an array param */
                        $$->value=1;
                        /*inherit the type*/
                        $$->isType=$1;
                        /*2 is used for IsAFunc to show its an array ref*/
                        $$->symbol=Insert($2,$1,2,level+1,1,offset,NULL);
                        offset+=1;
                        if(offset>maxoffset)
                            maxoffset = offset;
                    }
                    ;

compound-stmt: 
                    '{' { level++; } local-declarations statement-list '}'
                    {
                        $$=ASTCreateNode(BLOCK);
                        if($3 == NULL) /*dont add decls if null*/
                            $$->right=$4;
                        else {
                            /* add the stmtList to the end of the localDecls
                            and then make the link to the right of BLOCK
                            be the whole decls and stmtList*/
                            ASTattachleft($3,$4);
                            $$->right=$3;
                        }
                        /*delete the old symbols from this block so they can
                        be used in a different block later on*/
                        offset -=Delete(level);
                        level--;
                    }
                    ;

local-declarations: 
                    local-declarations var-declaration
                    {   /*check for null, if not attach it*/
                        if($1 != NULL){
                            $1->left=$2;
                            $$=$1;
                        }
                        else
                            $$=$2;
                    }
                    |   { $$ = NULL; }
                    ;

statement-list:     
                    statement-list statement
                    {   /*check for null, if not attch it*/
                        if($1 != NULL) {
                            $1->left=$2;
                            $$=$1;
                        }
                        else
                            $$=$2;
                    }
                    |   { $$ = NULL; }
                    ;

statement:          
                    expression-stmt
                    { /*everything here is just the simplification to general stmt
                        to be put into a stmtList above */
                        $$ = $1;
                    }
                    | compound-stmt
                    {
                        $$ = $1;
                    }
                    | selection-stmt
                    {
                        $$ = $1;
                    }
                    | iteration-stmt
                    {
                        $$ = $1;
                    }
                    | return-stmt
                    {
                        $$ = $1;
                    }
                    ;

expression-stmt: 
                    expression ';'
                    {
                        $$=ASTCreateNode(EXPRSTMT);
                        $$->right=$1;
                        $$->isType=$1->isType;
                    }                   
                    | ';'   { $$ = NULL; }
                    ;

selection-stmt:
                    T_IF '('expression')' statement %prec "then"
                    {
                        $$ = ASTCreateNode(IFSTMT);
                        /* right is the expression to be evaluated*/
                        $$->right=$3;
                        /* s1 is link to statment (it can be a block)*/
                        $$->s1=$5;
                    }
                    | T_IF '('expression')' statement T_ELSE statement
                    {
                        $$ = ASTCreateNode(IFSTMT);
                        /* right is the expression to be evaluated*/
                        $$->right=$3;
                        /* s1 is link to statment (it can be a block)*/
                        $$->s1=$5;
                        /* s2 holds the link to the else statment (can be a block)*/
                        $$->s2=$7;
                    }
                    ;

iteration-stmt: 
                    T_WHILE '('expression')' statement
                    {
                        $$ = ASTCreateNode(ITERSTMT);
                        /* right holds expression to be evaluated*/
                        $$->right=$3;
                        /* s1 holds the stmt to execute, can be block*/
                        $$->s1=$5;
                    }                   
                    ;

return-stmt: 
                    T_RETURN ';'    {$$ = ASTCreateNode(RETURNSTMT);}                           
                    | T_RETURN expression';'
                    {
                        $$ = ASTCreateNode(RETURNSTMT);
                        /*expression to return*/
                        $$->s2=$2;
                    }
                    ;

expression: 
                    var '=' expression
                    {
                        /*we dont have to check both for void because if only one
                        is void there will be a type mismatch, otherwise they are
                        the same. So if one is void the other is also*/
                        if (($1->isType != $3->isType) || ($1->isType == VOIDDEC))
                        {
                            yyerror("Type mismatch or void in Assignment");
                            exit(1);
                        }
                        $$=ASTCreateNode(ASSIGN);
                        /* hold the link to the var node*/
                        $$->right=$1;
                        /* hold the link to the expression statement*/
                        $$->s1=$3;
                        /*inherit the type, already check for equivalence
                        so can just use $1*/
                        $$->isType=$1->isType;
                        $$->name=CreateTemp();
                        $$->symbol=Insert($$->name,$$->isType,0,level,1,offset,NULL);
                        offset+=1;
                        if(offset>maxoffset)
                            maxoffset = offset;
                    }
                    | simple-expression     {$$ = $1;}
                    ;

var: 
                    T_ID
                    {
                        struct SymbTab * p;
                        if ((p=Search($1,level,1)) != NULL)
                        {
                            $$ = ASTCreateNode(IDENT);
                            $$->name=$1;
                            $$->symbol=p;
                            /*give the node op Type, based on SymbTab*/
                            $$->isType=p->Type;
                            if (p->IsAFunc == 2)
                            {
                                yyerror($1);
                                yyerror("Variable is an array, syntax error");
                            }
                        }
                        else
                        {
                            yyerror($1);
                            yyerror("undeclared variable used");
                            exit(1);
                        }
                    }
                    | T_ID '['expression']'
                    {
                        struct SymbTab * p;
                        if ((p=Search($1,level,1)) != NULL)
                        {
                            $$ = ASTCreateNode(IDENT);
                            $$->name=$1;
                            /* hold expression inside of array reference */
                            $$->right=$3;
                            $$->symbol=p;
                            /*capital Type is enum op*/
                            $$->isType=p->Type;
                            if (p->IsAFunc != 2)
                            {
                                yyerror($1);
                                yyerror("Variable is not an array, syntax error");
                            }
                        }
                        else
                        {
                            yyerror($1);
                            yyerror("undeclared variable used");
                            exit(1);
                        }
                    }
                    ;

simple-expression: 
                    additive-expression relop additive-expression
                    {  /* must ensure it is left recursive, to work properly */
                        /*we dont have to check both for void because if only one
                        is void there will be a type mismatch, otherwise they are
                        the same. So if one is void the other is also*/
                        if (($1->isType != $3->isType) || ($1->isType == VOIDDEC))
                        {
                            yyerror("Type mismatch or void in simpleExpression");
                            exit(1);
                        }
                        $$ = ASTCreateNode(EXPR);
                        $$->op=$2;
                        $$->left=$1;
                        $$->right=$3;
                        /*inherit the type, already check for equivalence
                        so can just use $1*/
                        $$->isType=$1->isType;
                        $$->name=CreateTemp();
                        $$->symbol=Insert($$->name,$$->isType,0,level,1,offset,NULL);
                        offset+=1;
                        if(offset>maxoffset)
                            maxoffset = offset;
                    }
                    | additive-expression   {$$ = $1;}
                    ;

relop: 
                    T_SMALLEQ       { $$=LESSTHANEQUAL; }
                    | T_SMALL       { $$=LESSTHAN; }
                    | T_GREAT       { $$=GREATERTHAN; }
                    | T_GREATEQ     { $$=GREATERTHANEQUAL; }
                    | T_COMPARE     { $$=EQUAL; }
                    | T_NOTEQ       { $$=NOTEQUAL; }
                    ;

additive-expression: 
                    additive-expression addop term
                    {   /* must ensure it is left recursive, to work properly */
                        /*we dont have to check both for void because if only one
                        is void there will be a type mismatch, otherwise they are
                        the same. So if one is void the other is also*/
                        if (($1->isType != $3->isType) || ($1->isType == VOIDDEC))
                        {
                            yyerror("Type mismatch or void in additive exp");
                            exit(1);
                        }
                        $$ = ASTCreateNode(EXPR);
                        $$->op=$2;
                        $$->left=$1;
                        $$->right=$3;
                        /*inherit the type, already check for equivalence
                        so can just use $1*/
                        $$->isType=$1->isType;
                        $$->name=CreateTemp();
                        $$->symbol=Insert($$->name,$$->isType,0,level,1,offset,NULL);
                        offset+=1;
                        if(offset>maxoffset)
                            maxoffset = offset;
                        }
                    | term  {$$ = $1;}                      
                    ;

addop:              
                    '+'     { $$ = PLUS; }
                    | '-'   { $$ = MINUS; }
                    ;

term: 
                    term mulop factor
                    {  /* must ensure it is left recursive, to work properly */
                        /*we dont have to check both for void because if only one
                        is void there will be a type mismatch, otherwise they are
                        the same. So if one is void the other is also*/
                        if (($1->isType != $3->isType) || ($1->isType == VOIDDEC))
                        {
                            yyerror("Type mismatch or void in term/factor exp");
                            exit(1);
                        }
                        $$ = ASTCreateNode(EXPR);
                        $$->op=$2;
                        $$->left=$1;
                        $$->right=$3;
                        /*inherit the type, already check for equivalence
                        so can just use $1*/
                        $$->isType=$1->isType;
                        $$->name=CreateTemp();
                        $$->symbol=Insert($$->name,$$->isType,0,level,1,offset,NULL);
                        offset+=1;
                        if(offset>maxoffset)
                            maxoffset = offset;
                    }                   
                    | factor    {$$ = $1;}                  
                    ;

mulop: 
                    '*'     { $$ = TIMES; }
                    | '/'   { $$ = DIVIDE; }
                    ;

factor: 
                    '('expression')'    {$$ = $2;}      
                    | var       { $$ = $1; }            
                    | call      { $$ = $1; }    
                    | T_NUM
                    {
                        $$=ASTCreateNode(NUMBER);
                        $$->value=$1;
                        /*numbers are always ints here*/
                        $$->isType=INTDEC;
                    }
                    ;

call: 
                    T_ID '('args')'
                    {
                        struct SymbTab * p;
                        if ((p=Search($1,0,1)) != NULL)
                        {   /*make sure symbol is a function*/
                            if(p->IsAFunc != 1)
                            {
                                yyerror($1);
                                yyerror("Is a variable, but was called as function");
                                exit(1);
                            }
                            /*have to make sure we are calling with right params*/
                            $$=ASTCreateNode(CALLSTMT);
                            /* hold the link to args in right*/
                            $$->right=$3;
                            $$->name=$1;
                            $$->symbol=p;
                            $$->isType=p->Type;
                        }
                        else
                        {
                            yyerror($1);
                            yyerror("Function not defined in symbol table");
                            exit(1);
                        }
                    }
                    ;

args: 
                    arg-list    { $$ = $1; }
                    |   { $$ = NULL; }
                    ;

arg-list: 
                    arg-list',' expression
                    {  /*attach the expressions to the tree in order
                        the use of the argList is handled above */
                        $$=ASTCreateNode(ARGLIST);
                        $$->left=$3;
                        $$->right=$1;
                    }
                    | expression
                    {
                        $$=ASTCreateNode(ARGLIST);
                        $$->right=$1;
                    }
                    ;
%%

int main(int argc, char *argv[]) {
    if (argc == 2)
        yyin = fopen(argv[1], "r");
    else {
        printf("No files - Exit\n");
        exit(1);
    }
    Display();
    yyparse();
    ASTprint(0, prog);
    return 0;
}

void yyerror(const char* s) {
    //fprintf(stderr, "%s-:%d %s\n", filename, yylineno, s);
    fprintf (stderr,"%s On Line: %d\n", s, yylineno);
}

В моем файле ast.h

#ifndef ASTH
#define ASTH
#include <stdio.h>
#include <malloc.h>

/* define the enumerated types for the AST.  THis is used to tell us what
sort of production rule we came across */

/* this is a partial list of NODE types for the ASTNode */

enum ASTtype {
   PROGRAM,
   VARDEC,
   ARGLIST,
   IDENT,
   BLOCK,
   NUMBER,
   ASSIGN,
   EXPRSTMT,
   FUNCTIONDEC,
   CALLSTMT,
   EXPR,
   IFSTMT,
   ITERSTMT,
   RETURNSTMT,
   PARAM,
   STRINGNODE
};


enum OPERATORS {
   PLUS,
   MINUS,
   TIMES,
   DIVIDE,
   LESSTHANEQUAL,
   LESSTHAN,
   GREATERTHAN,
   GREATERTHANEQUAL,
   EQUAL,
   NOTEQUAL,
   INTDEC,
   VOIDDEC,
   STRINGDEC,
   null
};

/* define a type AST node which will hold pointers to AST structs that will
   allow us to represent the parsed code
*/
struct ASTnode
{
     enum ASTtype type;
     enum OPERATORS op;
     enum OPERATORS isType;
     char * name;
     char * str;
     int value;
     struct SymbTab * symbol;
     struct ASTnode *left,*right;    /* left is usually the connector for statements */
     struct ASTnode *s1,*s2 ; /* used for holding IF and WHILE components -- not very descriptive */
};


struct ASTnode *prog;

//prototypes
struct ASTnode *ASTCreateNode(enum ASTtype mytype);

void ASTattachleft(struct ASTnode *p, struct ASTnode *q);

void ASTprint(int level, struct ASTnode *p);

#endif /* of ASTH */

Когда я пытаюсь скомпилировать, используя следующие команды:

bison -d step3.y
flex step3.fl
gcc step3.tab.c lex.yy.c -lfl -o a.out

Это дает мне следующую ошибку:

In file included from step3.fl:15:0:
step3.y:39:17: error: field ‘op’ has incomplete type
  enum OPERATORS op;

Я просмотрел весь интернет и не смог найти возможного исправления. Может ли кто-нибудь помочь мне с этим?

Заранее спасибо.

Ответы [ 2 ]

0 голосов
/ 04 ноября 2018

Проблема в том, что вам не хватает #include "ast.h" в вашем файле step3.fl. Должно быть перед строкой #include "step3.tab.h".

0 голосов
/ 04 ноября 2018

Код внутри %{ } будет включен только в сгенерированный файл .c, но не в сгенерированный заголовочный файл. Поэтому, когда ваш лексер включает файл заголовка (и ранее не включал ast.h), вы получаете объединение, но не содержимое ast.h, поэтому enum OPERATORS является неполным типом, потому что компилятор не видел определение OPERATORS.

В Bison обычно предпочтительнее использовать %code вместо %{ }, поскольку это дает вам больший контроль над тем, куда включен код. В вашем случае вы захотите включить ast.h в раздел %code requires. Из документов:

требуется

  • Язык (и): C, C ++
  • Цель: это лучшее место для написания кода зависимости, необходимого для YYSTYPE и YYLTYPE. Другими словами, это лучшее место для определения типов, на которые ссылаются директивы% union. Если вы используете #define, чтобы переопределить определения YYSTYPE и YYLTYPE по умолчанию в Bison, то это также лучшее место. Однако вы должны скорее% определить api.value.type и api.location.type.
  • Местоположение (я): Файл заголовка анализатора и файл реализации синтаксического анализатора перед определениями YYSTYPE и YYLTYPE, сгенерированными Bison.

Так это будет:

%code requires {
    #include "ast.h"
}
...