如果我有这个语法,例如
start : TKN id '{' '}' {cout<<$2<<endl;}
;
包含iostream,TKN声明为令牌,id类型声明为char *
作为输入我输入tkn aaa { }
输出不应该是aaa
??有时,它会打印}
和0
,有时会挂起
我想获得id
的值,如何正确?
%{
#include "yacc.hpp"
#include <math.h>
#include<iostream>
#include<string>
int rows = 1,tmp=0;
%}
Id [a-zA-Z_][0-9a-zA-Z_]*
%x String ...
%option c++
%option noyywrap
%%
{Id} {strcpy(yylval.strVal,yytext);cout<<"lex= "<<yytext<<endl;return Id;}//output line 1
...
%output ="yacc.cpp"
%defines
%verbose
%token Id
%token NAMESPACE
%union{
int iVal;
float fVal;
char* strVal;
class Symbol* symPtr;
class NS* nsPtr;
};
%token <iVal> INT_VAL;
%token <fVal> F_VAL;
%token <strVal> STR_VAL INT FLOAT STRING Id ;
%type <nsPtr> namespacedecl
%type <symPtr> ns_closer
%type <strVal> Q_ID
//-------------------------------------------
namespacedecl : NAMESPACE Q_ID '{' '}' {cout<<"ns= "<<$2<< endl ;} // output line 3
| NAMESPACE Q_ID '{' typedecl_closer '}' ;
Q_ID : Q_ID '.' Id {cout<<$3<< endl ;$$ = $3;}
| Id {$$ = $1;cout<<"qid="<<$$<<endl;} // output line 2
当然文件比这个大,但复制/粘贴一切都会让你迷路^^
如果有办法附加文件,请告诉我,因为我现在还是新手,这比复制/粘贴容易得多。
这就是我跑步时得到的:
感谢您的回复
答案 0 :(得分:1)
这取决于'id'规则返回的内容!
%%
start : TKN id '{' '}' {cout<<$2<<endl;} ;
id : ID { return "XXXXX"; } // What is returned here
// Is what will be printed out by $2
%%
注意:
惯例是终端令牌是全部大写(TKN)。而非终端令牌是小写(id)。根据这个惯例,我希望id能有一个如何扩展的规则。
我怀疑你在做的是:
%%
id : ID { return yytext; }
%%
哪个是指向lex缓冲区的指针。这是一个易失性缓冲区。您可以不依赖其内容保持不变(也不能依赖它'\ 0'终止)。您需要做的是在您识别它的位置复制令牌。
%%
id : ID { return strndup(yytext, yylen); }
%%
在这些方面:
{Id} { strcpy(yylval.strVal,yytext);
cout<<"lex= "<<yytext<<endl;
return Id;
}
strcpy()可能不好。
cout是危险的,因为你不能依赖yytext被'\ 0'终止。
我愿意:
{ID} { cout << "lex=" << std::string(yytext,yytext+yylen); // use yylen
return Id;
}
不要乱用lex中的yacc结构。它将你的lex文件紧密地耦合到yacc(这不是必需的)。只需返回令牌。然后yacc可以手动获取令牌值。
然后在YACC档案中:
Q_ID : Q_ID '.' ident {$$ = $3; cout<<"Q.id="<<$$<<endl;}
| ident {$$ = $1; cout<<"ID ="<<$$<<endl;}
ident : Id {$$ = strndup(yytext, yylen);}
对于具有长令牌的每个终端(Id)具有用于对终端进行解码并在yacc联合结构中生成正确值的非终端。在这种情况下,我们有ident而不是终端。它只是解码Id终端并正确设置令牌(此Id在union结构中没有类型)。
这一行:
%token <strVal> STR_VAL INT FLOAT STRING Id ;
看起来不对,但很难说不知道INT,FLOAT和STRING是什么。我猜这些是关键字int,float,string的终端标记。在这种情况下,您不需要存储实际的令牌字符串。您知道它是INT / FLOAT或STRING的事实就足够了。
这应该是:
%token <strVal> STR_VAL;
%token <strVal> ident;
答案 1 :(得分:0)
这在很大程度上取决于你的词法分析器作为id
标记的yylval返回的内容,因为这是在转移标记时被复制到解析器堆栈上的内容,因此$2
指的是规则被评估。猜测一下,你有一个lex规则,如:
[a-zA-Z_][a-zA-Z_0-9]* { yylval.str = yytext; return id; }
与
%token<str> id
解析器中的。在这种情况下,您将指针存储到flex的内部扫描程序缓冲区中,该缓冲区在该时刻包含“aaa”,但将被稍后的标记覆盖,因此在操作运行时,$ 2指针指向其他位置。您需要将字符串复制到不会被覆盖的位置,并将yylval设置为指向该字符串。你可以使用strdup(3)将字符串复制到malloced缓冲区,这可以解决这个问题,但可能会让你内存泄漏。
修改强>
有了您的其他信息,令您惊讶的是您的程序没有崩溃 - 您strcpy
将令牌文本yylval.strVal
转换为yylval.Strval
,但您永远不会将yylval.strVal = malloc(strlen(yytext)+1);
初始化为指向任何地方,所以你将它复制到一些随机的内存位置。你需要在lex.l动作中加入类似[a-zA-Z_][a-zA-Z_0-9]* { yylval.str = strdup(yytext); return id; }
的东西,以确保它指向有效的内存,或者只使用更简单和等效的strdup调用,因为它结合了malloc和strcpy:
{{1}}