对 `yylex` 的未定义引用

3

我正在尝试使用flex和bison解析一个输入文件,但在编译程序时遇到了困难。我附上我的flex和bison代码以及错误信息。

请帮助我解决这些错误。

lex.l

%{
 #include <iostream>  
 #include <stdio.h>
#include "yacc.tab.h"
 #define YY_DECL extern "C" int yylex()

using namespace std;  


%}

DOT             "."  
COLON           ":"  
SEMICOLON       ";"  
COMMA           ","  
ANGLE_LEFT      "<"  
ANGLE_RIGHT     ">"  
AT              "@"  
EQUAL           "="  
SQUARE_OPEN     "["  
SQUARE_CLOSE    [^\\]"]"  
OPENBRACE       "\("  
CLOSEBRACE      "\)"  
QUOTE           "\""  
QUOTE_OPEN      "\""  
QUOTE_CLOSE     [^\\]"\""  
SPACE           " "  
TAB             "\t"  
CRLF            "\r\n"  
QUOTED_PAIR     "\\"[^\r\n]  
DIGIT           [0-9]  
ALPHA           [a-zA-Z]  
QTEXT           [0-9a-zA-Z!#$%&'()*+,\-.\/:;<=>?@\[\]^_`{|}~]  

%%

[a-zA-Z0-9]+            { yylval.sval = strdup(yytext); return TOK_STRING; }

{SPACE}*                    {return TOK_SPACE; }

{SPACE}*Name.*              {return TOK_NAME; }  
{SPACE}*SIZE.*              {return TOK_SIZE; }  
{SPACE}*ITERATE.*           {return TOK_ITERATE; }  
{SPACE}*DIRECTION.*     {return TOK_DIRECTION; }  

^{CRLF}                         { return TOK_EMPTY_LINE; }  
{CRLF}                          {}  
.                               {}/* ignore unknown chars */  

yacc.y

 %{
 #include <cstdio> 
 #include <cstring>
 #include <iostream>
 #include <stdio.h>

using namespace std;

extern "C" int yylex();  
extern "C" FILE *yyin;

void yyerror(const char* s);  


%}

%union  
{  
    char* sval;  
};  

%token <sval> TOK_NAME  
%token <sval> TOK_SIZE  
%token <sval> TOK_STRING  
%token <sval> TOK_ITERATE  
%token <sval> TOK_DIRECTION  


%token TOK_SPACE  


%%

str:  
    TOK_SPACE TOK_NAME TOK_SPACE TOK_STRING   
    {  
        cout << "Value:" << $2 << "->" << $4;  
    }  
    ;  
%%  

int main(void) {  
    FILE * pt = fopen("new file ", "r" );  
    if(!pt)  
    {  
    cout << "Bad Input.Noexistant file" << endl;  
    return -1;  
    }  
    yyin = pt;  
    do  
    {  
        yyparse();  
     }while (!feof(yyin));        
}  
void yyerror(const char *s)  
{  
   cout << "Error. " << s << endl;   
   exit(-1);     
}  

我使用以下技术构建这些内容:

flex bas.l 
bison -d yacc.y 
g++ lex.yy.c yacc.tab.c -lfl -o scanner.exe 

编译程序时,我发现以下错误:

/tmp/cceIyDkD.o: 在函数 `main' 中:
yacc.tab.c:(.text+0x708): 对 `main' 的多次定义
/tmp/ccatq95p.o:lex.yy.c:(.text+0x1228): 此处首次定义
/usr/bin/ld: 警告:符号 `main' 的大小已从 /tmp/ccatq95p.o 中的 86 更改为 /tmp/cceIyDkD.o 中的 120
/tmp/cceIyDkD.o: 在函数 `yyparse()' 中:
yacc.tab.c:(.text+0x2d6): 对 `yylex' 的未定义引用
collect2: ld 返回了 1 个退出状态

请帮助我解决这个问题。

4
这是链接器错误。如果您能展示您的链接器命令,会有所帮助。 - Tom Karzes
1
flex bas.l bison -d yacc.y g++ lex.yy.c yacc.tab.c -lfl -o scanner.exe - shailavi shah
g++ --version 命令打印的第一行是什么?我认为你可能正在使用一个非常老的编译器,这可能会导致一些问题。(另外,你的问题中说你的扫描器文件名为 lex.l,但你调用了 flex bas.l。我假设那只是一个笔误。) - rici
我解决了这个问题。非常感谢你的帮助。 - shailavi shah
3个回答

8

我曾经遇到与此类似的问题,后来发现 flex 2.6.x 改变了 yylex 的行为方式。我通过使用旧版本的 Flex 来解决了这个问题。在 Ubuntu 中,有一个叫做 flex-old 的包含旧版本的软件包。将 flex 替换为 flex-old 对我来说解决了这个问题。


谢谢!对我来说,降级到“flex-old”软件包也是解决方案。 - Anthony DeRosa
不用改变您的 make 文件,这个解决方案的一个好处是因为 flex 和 flex-old 都可以通过终端中的 flex 命令访问。不客气! - Gustavo Oliveira
3
最好包括一个描述2.6.x中发生了什么变化的说明,或者至少为那些更愿意修复其代码以适应新版本flex的人提供一个链接。 - Stuart M

3
  1. 在parser.y文件中,更准确地说是在包含内容的C代码中,您需要编写:extern int yylex(void);。不需要使用Extern关键字,因为默认情况下全局函数具有动态连接性,但是写入关键字以指示该函数不来自您正在处理的文件是一个好习惯。然而,在这里您的代码没有错误。

  2. 将lex.yy.c编译为对象文件,将y.tab.c编译为对象文件,然后将它们链接在最终程序中。首先执行yacc命令非常重要,因为Lex使用由Yacc生成的yacc.tab.h。您会收到错误,因为您对其进行了弯曲,但您不知道yacc.tab.h是什么。

    yacc -d yacc.y 创建y.tab.c

    g++ -c y.tab.c -o y.tab.o 将y.tab.c编译为对象文件y.tab.o

    flex lex.l 创建lex.yy.c

    g++ -c lex.yy.c -o lex.yy.o 将lex.yy.c编译为对象文件lex.yy.o

    g++ lex.yy.o y.tab.o -o program 将它们链接在一起。

注意:请确保在您的lex文件中包含其他包含文件之后包含“y.tab.h”,因为可能其他包含使用在y.tab.h中声明的某些函数,请牢记这一点。当您编写更复杂的解析器时,可能会出现这种情况。


2

您的代码中存在一些错误,导致无法编译。您需要在文件yacc.y中添加以下内容:

%token <sval> TOK_EMPTY_LINE 

最后:

#include "lex.yy.c"

并以此方式构建:

flex bas.l 
bison -d yacc.y 
g++ yacc.tab.c -lfl -o scanner.exe 

现在您会发现它已经编译成功了。

需要注意的是,我在问题中删除了您词法分析器源代码中的一些拼写错误。并不是所有规则都从第一列开始。我删除了一些前导空格,因为它们是拼写错误。


1
它在我的系统上编译并运行。您能指出您正在运行什么操作系统和软件吗?这可能有助于解释不同的结果。 - Brian Tompsett - 汤莱恩
bas.l: 在函数“int main()”中: bas.l:46: 错误:重定义了“int main()” yacc.y:40: 错误:此处先前已定义“int main()” - shailavi shah
@shailavishah 请回答 @rici 提出的问题:g++ --version 的结果是什么?这可能与原因有关... 当我们拥有足够的事实时,问题可以解决。 - Brian Tompsett - 汤莱恩
好的...我明白问题了,非常感谢你的帮助。 - shailavi shah
@shailavishah 都解决了吗?太好了。如果你有解决方案,请接受答案。 - Brian Tompsett - 汤莱恩
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接