lex / yacc没有成员名称和声明错误

时间:2014-11-17 03:13:02

标签: c++ parsing map yacc lex

我遇到了这个lex / yacc计算器的问题我正在努力。我尝试使用字符串作为键并使用double作为值来实现STL映射来处理我的变量。

我最初将变量存储为数组中的单个字符,一切正常。现在,我正在尝试实施地图,我已经运行了这些错误消息。

为什么我收到联合错误消息?我也不承认其他错误。 (我稍后会对那些已弃用的转换错误进行排序)

错误消息

flex calc.l
yacc calc.y
g++ -o calc lex.yy.c y.tab.c

In file included from calc.l:4:
y.tab.h:7: error: 'string' does not name a type
calc.l: In function 'int yylex()':
calc.l:12: error: 'union YYSTYPE' has no member named 'index'
calc.l:12: error: expected ';' before ')' token
calc.l:16: error: 'yyerror' was not declared in this scope
y.tab.c: In function 'int yyparse()':
y.tab.c:270: error: 'yylex' was not declared in this scope
y.tab.c:308: warning: deprecated conversion from string constant to 'char*'
calc.y:33: error: cannot convert 'double' to 'std::string*' in assignment
y.tab.c:460: error: 'yylex' was not declared in this scope
y.tab.c:494: warning: deprecated conversion from string constant to 'char*'

calc.l

%{
#include <cstdlib>
#include <string.h>
#include "y.tab.h"


%}
%%
"print"                     {return print;}
"exit"                      {return exit_command;}
[0-9]+                      {yylval.num = strtod(yytext, NULL); return number;}
[_[:alpha:]][_[:alnum:]]*   {yylval.index = new std::string(yytext)); return identifier; }
[ \t]                       ;
[\n]                        {return(CR);}
[-()+*/;=]                  {return yytext[0];}
.                           {ECHO; yyerror ("unexpected character");}

%%
int yywrap (void) {return 1;}

calc.y

%{
void yyerror (char *s);
#include <stdio.h>     /* C declarations used in actions */
#include <stdlib.h>
#include <string>
#include <map>

static std::map<std::string, double> vars;
%}

%union {double num; std::string *index;}         /* Yacc definitions */
%start line
%token print
%token CR
%token exit_command
%token <num> number
%token <index> identifier
%type <num> line exp term 
%type <index> assignment

%%

/* descriptions of expected inputs     corresponding actions */

line    : assignment CR         {;}
        | exit_command CR       {exit(EXIT_SUCCESS);}
        | print exp CR          {printf("Printing %f\n", $2);}
        | line assignment CR    {;}
        | line print exp CR     {printf("Printing %f\n", $3);}
        | line exit_command CR  {exit(EXIT_SUCCESS);}
        ;

assignment : identifier '=' exp  {$$ = vars[*$1] = $3; delete $1; }
            ;
exp     : term                  {$$ = $1;}
        | exp '+' term          {$$ = $1 + $3;}
        | exp '-' term          {$$ = $1 - $3;}
        | exp '*' term          {$$ = $1 * $3;}
        | exp '/' term          {$$ = $1 / $3;}
        | '(' exp ')'           {$$ = $2;}
        ;
term    : number                {$$ = $1;}
        | identifier            {$$ = vars[*$1];      delete $1; } 
        ;

%%                    

int main (void) {


    return yyparse ( );
}

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

1 个答案:

答案 0 :(得分:3)

yacc / bison中的%union指令导致union类型的定义(通常称为union YYSTYPE);如果yacc / bison产生头文件(y.tab.h),那么语义联合(union YYSTYPE)的定义将被放入该头中。因此,%union指令中使用的每个类型都必须在导入y.tab.h的任何文件中可用,也就是说您的flex文件。但是,该文件不#include <string>,因此std::string不可用。 (请继续阅读。显而易见的修复不起作用。)

我无法重现错误消息y.tab.h:7: error: 'string' does not name a type - 在我尝试的yacc和bison的版本中,错误消息稍后会显示在文件中,并引用std::string;因为你没有在命令中显示-d选项,我想知道你是否使用了生成的头文件的先前版本。 (总的来说,make会比每次尝试记住正确的命令序列更好。)无论如何,至少有一些其他错误是{{1}声明中的错误的结果。 } type。

但如果您想在词法操作中使用union,则需要#include <string>,但您将无法将std::string作为语义联合的一部分。至少,你不能使用标准的yacc / bison模板做到这一点;您可以使用std::string模板使用最新版本的bison,但我怀疑您没有足够新的C++版本。

你不能在语义联合中使用bison的原因正是它是一个联合。在C ++中,联合类型不应包含任何具有非平凡析构函数的类型,因为当联合类型本身被破坏时,不会运行成员析构函数。反过来,这是因为当工会被破坏时,无法知道哪个工会成员实际上是有效的。如果您知道如何解决这个问题,可以为std::string定义一个显式构造函数,但在这种情况下,union声明是自动生成的(没有任何构造函数或析构函数,因为解析器生成器是生成C代码),即使您使用自己的声明(可能使用最近的bison版本),当从堆栈中弹出值时,弄清楚哪个联合成员处于活动状态并非易事。 (同样,从理论上讲,野牛可能会为你解决这个问题,但事实并非如此。)

所以你坚持使用“普通旧数据”(POD)类型作为语义联合成员。虽然union绝对不是POD,但std::string是。std::string*。 (任何指针都是POD类型,无论它指向的对象有多么简单。)因此,您可以使index成员成为std::string*,然后使用{{1}显式管理创建和销毁}和new。这与使用deletestrdup的纯C解决方案没有太大区别。但你必须做两个中的一个。

不幸的是,管理语义值的生命周期可能并非易事,因为总是很容易通过浅拷贝而不是深拷贝来共享内存。但是,在像你这样的简单语法中,避免拥有一个以上语义值的活动实例并不困难。 (freeunique_ptr也不是POD类型,所以你不能使用C ++编译器来强制指针的唯一性,或者自动引用计数它。抱歉。)如果你从来没有两个相同值的活动副本,你可以使用一个非常简单的启发式,只要你严格遵守纪律,它就会起作用:

在每个操作中,对于需要显式销毁的每个语义值shared_ptr

  1. 如果将值合并到某个语义类型结构中,那很好,因为解析器会在操作结束时弹出堆栈,因此值既在堆栈上又存储在其他堆栈中结构非常短暂。

  2. 如果该值未包含在任何更持久的存储中,则会破坏该操作中的值。同样,该值即将从堆栈中弹出,因此悬空指针将非常短暂。

  3. 对于$n自动弹出堆栈而不运行操作的情况,请使用bison %destructor指令。 (主要是错误恢复。)