我遇到了这个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);}
答案 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
。这与使用delete
和strdup
的纯C解决方案没有太大区别。但你必须做两个中的一个。
不幸的是,管理语义值的生命周期可能并非易事,因为总是很容易通过浅拷贝而不是深拷贝来共享内存。但是,在像你这样的简单语法中,避免拥有一个以上语义值的活动实例并不困难。 (free
和unique_ptr
也不是POD类型,所以你不能使用C ++编译器来强制指针的唯一性,或者自动引用计数它。抱歉。)如果你从来没有两个相同值的活动副本,你可以使用一个非常简单的启发式,只要你严格遵守纪律,它就会起作用:
在每个操作中,对于需要显式销毁的每个语义值shared_ptr
:
如果将值合并到某个语义类型结构中,那很好,因为解析器会在操作结束时弹出堆栈,因此值既在堆栈上又存储在其他堆栈中结构非常短暂。
如果该值未包含在任何更持久的存储中,则会破坏该操作中的值。同样,该值即将从堆栈中弹出,因此悬空指针将非常短暂。
对于$n
自动弹出堆栈而不运行操作的情况,请使用bison %destructor
指令。 (主要是错误恢复。)