unresolved external symbol for yyparse()

2019-08-08 23:35发布

问题:

My main.cpp looks like this:

#include <assert.h>
#include <fstream>
#include <iostream>
#include "astgen.h"
#include "astexec.h"

extern int yyparse();
extern FILE *yyin;

int main()
{
    //yydebug = 0;
    struct AstElement *a = 0;
    FILE *fp ;
    fopen_s(&fp,"example.txt","r");
    //std::cout<<fp;
    if (fp==NULL)
    {
        std::cout<<"error";
    }
    yyin=fp;
    yyparse();

    assert(a);
    struct ExecEnviron* e = createEnv();
    execAst(e, a);
    freeEnv(e);
    /* TODO: destroy the AST */
}

When I try to compile this, I get the error: main.obj : error LNK2019: unresolved external symbol "int __cdecl yyparse(void)" (?yyparse@@YAHXZ) referenced in function _main

I am a newbie in flex/bison. Any help would be appreciated. If it helps here is my parser.y file:

%error-verbose /* instruct bison to generate verbose error messages*/
%{
#include "astgen.h"
#define YYDEBUG 1

/* Since the parser must return the AST, it must get a parameter where
 * the AST can be stored. The type of the parameter will be void*. */
#define YYPARSE_PARAM astDest
extern int yylex();
%}

%union {
    int val;
    char op;
    char* name;
    struct AstElement* ast; /* this is the new member to store AST elements */
}

%token TOKEN_BEGIN TOKEN_END TOKEN_WHILE TOKEN_DO
%token<name> TOKEN_ID
%token<val> TOKEN_NUMBER
%token<op> TOKEN_OPERATOR
%type<ast> program block statements statement assignment expression whileStmt call
%start program

%{
/* Forward declarations */
void yyerror(const char* const message);


%}

%%

program: statement';' { (*(struct AstElement**)astDest) = $1; };

block: TOKEN_BEGIN statements TOKEN_END{ $$ = $2; };

statements: {$$=0;}
    | statements statement ';' {$$=makeStatement($1, $2);}
    | statements block';' {$$=makeStatement($1, $2);};

statement: 
      assignment {$$=$1;}
    | whileStmt {$$=$1;}
    | block {$$=$1;}
    | call {$$=$1;}

assignment: TOKEN_ID '=' expression {$$=makeAssignment($1, $3);}

expression: TOKEN_ID {$$=makeExpByName($1);}
    | TOKEN_NUMBER {$$=makeExpByNum($1);}
    | expression TOKEN_OPERATOR expression {$$=makeExp($1, $3, $2);}

whileStmt: TOKEN_WHILE expression TOKEN_DO statement{$$=makeWhile($2, $4);};

call: TOKEN_ID '(' expression ')' {$$=makeCall($1, $3);};

%%

#include "astexec.h"
#include <stdlib.h>

void yyerror(const char* const message)
{
    fprintf(stderr, "Parse error:%s\n", message);
    exit(1);
}

EDIT 1: I am using winflexbison in VS 2012 environment to generate my parser and the lexer. The process for the build is rather simple. All that is required to be done is to add the <appropriate>_custom_build.targets to build customizations for the project in VS and just build solution. A reference to the documentation can be found here @ winflexbison for Visual studio.

I would also like to point out that I followed the same method to build a sample flex and bison file and later integrated in a project. That worked fine for me.

What am I doing wrong here?

回答1:

Your bison input includes a well-commented but obsolete definition [note 1]:

/* Since the parser must return the AST, it must get a parameter where
 * the AST can be stored. The type of the parameter will be void*. */
#define YYPARSE_PARAM astDest

As the comment indicates, this will cause the prototype for yyparse to be:

int yyparse(void* astDest);

However, in your main function, you've declared

extern int yyparse();

Since you are compiling this with C++, the yyparse declared in main.cpp and invoked by main is not the same function as the yyparse declared in parser.tab.h and defined in parser.tab.cpp. Consequently, you get a linker error.

Had you put #include "parser.tab.h" into your main.cpp file instead of manually declaring yyparse, you would probably have seen a more easily-understood error message, although I can't vouch for error messages produced by VS 2012.


Notes:

  1. YYPARSE_PARAM has been deprecated since bison version 1.875, well more than a decade ago, and it currently only works with yacc-compatible parsers. You should use the %parse-param declaration, which works with all bison-produced parsers, and which allows you to specify an actual parameter type rather than having to abandon type safety. See the bison manual for details.


回答2:

You need to use Bison to generate a C file from your grammar file. Then you need to compile that C file (which will be named y.tab.c by default) and link it with your C file that contains your definition of main.