yacc 和 lex 中的 yytext 累积问题
yytext accumulation problam in yacc and lex
我正在尝试打印 AST 并在树中打印标识符的实际名称。
我正在使用 lex 和 yacc。
出于某种原因,yacc 提前读取一行中的所有标记,直到“;”这使我无法使用 yytext 访问标识符的文本值。
这是 lex 文件:
%{
%}
%%
"&&" {return OP_AND;}
\"|"\"|" {return OP_OR;}
!= {return OP_NE;}
== {return OP_EQ;}
> {return OP_GT;}
>= {return OP_GE;}
\< {return OP_LT;}
\<= {return OP_LE;}
! {return OP_NOT;}
= {return ASSIGN;}
\+ {return PLUS;}
\- {return MINUS;}
\* {return MULT;}
\/ {return DIV;}
\& {return ADRS;}
\^ {return PTR_VAL;}
\; {return SEMI;}
, {return COMMA;}
\{ {return CURLY_O;}
\} {return CURLY_C;}
\( {return PAREN_O;}
\) {return PAREN_C;}
\| {return BAR;}
\[ {return SQR_O;}
\] {return SQR_C;}
else {return ELSE;}
if {return IF;}
do {return DO;}
for {return FOR;}
bool {return BOOL;}
void {return VOID;}
int { return INT;}
charp {return CHARP;}
intp {return INTP;}
string {return STRING;}
while {return WHILE;}
true {return TRUE_;}
false {return FALSE_;}
return {return RETURN;}
null {return NULL_;}
0|[1-9][0-9]* {return CONST_INT;}
[a-zA-Z][a-zA-Z0-9]* { return IDENT;}
\'.\' {return CONST_CHAR; }
\".*\" {return CONST_STRING; }
\[$([^$]*$+[^\]])*[^$]*$+\] { /*comment*/}
[ \n\t] { /* skip whitespace */}
. {return ILLEAGAL;}
%%
这是 yacc 文件:
* 请注意,有些规则尚未完全准备好,但未被此输入文件使用,与本案例无关。
%{
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
extern char * yytext;
typedef struct node
{
char * token;
struct node *b1;
struct node *b2;
struct node *b3;
struct node *b4;
}node;
int yylex();
int yyerror();
struct node* mknode2(char* token,node* b1,node* b2);
struct node* mknode3(char* token,node*b1,node*b2,node*b3);
struct node* mknode4(char* token,node*b1,node*b2,node*b3,node*b4);
int printtree(node* tree);
#define YYSTYPE node*
%}
%token SEMI COMMA CURLY_O CURLY_C PAREN_O PAREN_C SQR_O SQR_C BAR
%token BOOL INT CHAR STRING INTP CHARP VOID
%token IF DO WHILE FOR RETURN
%token CONST_INT CONST_CHAR CONST_STRING NULL_ IDENT TRUE_ FALSE_
%token ILLEAGAL
%right ASSIGN
%left OP_OR OP_AND OP_NE OP_EQ OP_GT OP_GE OP_LT OP_LE
%left PLUS MINUS
%left MULT DIV
%right OP_NOT
%right ADRS
%right PTR_VAL
%nonassoc LOWER_THAN_ELSE
%nonassoc ELSE
%start program
%%
program : functions {printtree();}
;
functions : functions function {$$ = mknode2("FUNC" ,,);}
| {$$ = NULL;}
;
function : type id PAREN_O f_params PAREN_C CURLY_O body CURLY_C { $$ = mknode3(,,,);}
;
body : var_decls stmts ret_stmt {$$ = mknode3("BODY" , , , );}
;
nest_block : stmts {$$=;}
;
f_params : f_params_ {$$ = ;}
| {$$ = NULL;}
;
f_params_ : f_params_ COMMA param {$$ = mknode2("," , , );}
| param {$$ = ;}
;
param : type id {$$ = mknode2( , NULL , );}
;
var_decls : var_decls var_decl { $$ = mknode2("DECL" , , );}
| var_decls function {$$ = mknode2("DECL" , , );}
| {$$ = NULL;}
;
var_decl : type var_list SEMI { $$ = mknode2( , NULL , ); }
;
var_list : var_list COMMA dec_assign {$$ = mknode2(",", , );}
| dec_assign { $$ = ;}
;
dec_assign : id ASSIGN expr { $$ = mknode2("=", , );}
| id {$$ = ; }
;
type : BOOL {$$ = yytexy;}
| INT { $$ = yytext;}
| CHAR {$$ = yytext;}
| INTP {$$ = yytext;}
| CHARP {$$ = yytext;}
| STRING {$$ = yytext;}
| VOID {$$ = yytext;}
;
stmts : stmts stmt { $$ = mknode2("STMT" , , );}
| { $$ = NULL ;}
;
stmt : assignment SEMI {$$ = ;}
| fct_call SEMI {$$ = ; }
| IF PAREN_O expr PAREN_C opt_nest %prec LOWER_THAN_ELSE {$$ = mknode2("if" , , );}
| IF PAREN_O expr PAREN_C opt_nest ELSE opt_nest {$$ = mknode3("if" , , , );}
| WHILE PAREN_O expr PAREN_C opt_nest {$$ = mknode2("while" , , );}
| DO CURLY_O nest_block CURLY_C WHILE PAREN_O expr PAREN_C SEMI {$$ = mknode2("do" , , );}
| FOR PAREN_O assignment SEMI expr SEMI assignment PAREN_C opt_nest {$$ = mknode4("for" , , , , );}
;
opt_nest : CURLY_O nest_block CURLY_C {$$ = ;}
| stmt {$$ = ;}
;
ret_stmt : RETURN expr SEMI { $$ = mknode2("return" , NULL , );}
| { $$ = NULL; }
;
assignment : id ASSIGN expr {$$ = mknode2("=" , , );}
| id SQR_O expr SQR_C ASSIGN expr {$$ = mknode3("=" , , , );}
;
fct_call : id PAREN_O expr_list PAREN_C {$$ = mknode2("FUNCALL" , , );}
;
expr_list : expr_list_ expr {$$ = mknode2("AGRS" , , );}
| {$$ = NULL;}
;
expr_list_ : expr_list_ expr COMMA { $$ = mknode2("," , , );}
| {$$ = NULL;}
;
id : IDENT { $$ = mknode2(strdup(yytext) , NULL , NULL);}
;
expr : expr PLUS expr { $$ = mknode2("+" , , );}
| expr MINUS expr { $$ = mknode2("-" , , );}
| expr MULT expr { $$ = mknode2("*" , , );}
| expr DIV expr { $$ = mknode2("/" , , );}
| expr OP_AND expr { $$ = mknode2("&&" , , );}
| expr OP_OR expr { $$ = mknode2("||" , , );}
| expr OP_NE expr { $$ = mknode2("!=" , , );}
| expr OP_EQ expr {$$=mknode2("==",,);}
| expr OP_GT expr {$$=mknode2(">",,);}
| expr OP_GE expr {$$=mknode2(">=",,);}
| expr OP_LT expr {$$=mknode2("<",,);}
| expr OP_LE expr {$$=mknode2("<=",,);}
| OP_NOT expr {$$=mknode2("!",NULL,);}
| PTR_VAL expr {$$=mknode2("^",NULL,);}
| ADRS expr {$$=mknode2("&",NULL,);}
| MINUS expr {$$=mknode2("-",NULL,);}
| literal {$$=mknode2(,NULL,NULL);}
| fct_call {$$= ;}
| id {$$= ;}
| PAREN_O expr PAREN_C {$$=mknode2("(_)",NULL,);}
| BAR expr BAR {$$=mknode2("|_|",NULL,);}
;
literal : CONST_INT {$$ = "const_int";}
| TRUE_ { $$ = "true" ;}
| FALSE_ { $$ = "false" ;}
| CONST_CHAR { $$ = "const_char" ;}
| CONST_STRING { $$ = "const_string" ;}
| NULL_ { $$ = "null" ;}
;
%%
#include "lex.yy.c"
void main(){
return yyparse();
}
int tabCount =0;
struct node* mknode2(char* token,node*b1,node*b2)
{
node* newnode=(node*)malloc(sizeof(node));
char* newstr;
if( token ){
printf("token: %s \n" , token);
newstr=(char*)malloc(sizeof(token)+1);
newstr[sizeof(token)]='[=11=]';
strcpy(newstr,token);
}
else{
newstr=(char*)malloc(3);
strcpy(newstr,"AA");
}
newnode->b1=b1;
newnode->b2=b2;
newnode->b3=NULL;
newnode->b4=NULL;
newnode->token=newstr;
return newnode;
}
struct node* mknode3(char* token,node*b1,node*b2,node*b3)
{
node* newnode= mknode2(token,b1,b2);
newnode->b3=b3;
return newnode;
}
struct node* mknode4(char* token,node*b1,node*b2,node*b3,node*b4)
{
node* newnode= mknode3(token,b1,b2,b3);
newnode->b4=b4;
return newnode;
}
void printTabs(){
int i;
for(i=0;i<tabCount;i++){
printf("\t");
}
}
int printtree(node* tree)
{
tabCount++;
printTabs();
printf("%s\n",tree->token);
if(tree->b1)printtree(tree->b1);
if(tree->b2)printtree(tree->b2);
if(tree->b3)printtree(tree->b3);
if(tree->b4)printtree(tree->b4);
tabCount--;
return 1;
}
int yyerror(){
printf("ERROR!\n");
return 0;
}
这是输入文件:
void main(int x){
int y,w,r; int z;
}
这是预先排序的输出 AST:
FUNC
void
main
int x
x
BODY
DECL
int z;
;
DECL
int y,w,r;
,
;
,
,
,
我期望的输出:
FUNC
void
main
int
x
BODY
DECL
int
z
DECL
int
,
y
,
w
r
当我得到规则时可以看到:在我的 yacc 文件中键入 yytext 包含整个句子("int y,w,r;" 而不仅仅是 "int")当我得到规则时:id它只包含一个标记,恰好是一个标记向前,然后是预期的标记。
- 请注意,在函数名称和函数参数中,yytext 可以正常工作。
- 我尝试从 lex 文件打印 yytext 并且标记似乎包含正确的值。
yytext
指向最后扫描的词位。之前保存到扫描器内部缓冲区的任何其他指针(例如 yytext
的先前值)都是无效的,并且不会假定指向任何有用的东西。
在解析器操作中,您几乎不知道最后扫描的标记是什么。 Yacc/bison 生成 LALR(1) 解析器,其中 1
表示 "one lookahead token"。这意味着解析器(可以)始终检查 next 标记,如果它这样做,那么 yytext
将指向标记。一些实现总是预读;其他人,如野牛,如果前瞻不会改变解析器行为,有时不会提前阅读。因此,在解析器操作期间 yytext
指向哪个标记 未指定 并且可能会在未来版本或不同的实现中发生变化。
底线:从不在解析器操作中使用yytext
。永远不要将 yytext
的指针值从扫描器传递给解析器。如果您将需要解析器中令牌的字符串值,复制该字符串,然后通过yylval
将副本的地址传递给解析器。如果您使用 strdup
或类似的,不要忘记 free
副本,当您不再需要它时。
我正在尝试打印 AST 并在树中打印标识符的实际名称。 我正在使用 lex 和 yacc。
出于某种原因,yacc 提前读取一行中的所有标记,直到“;”这使我无法使用 yytext 访问标识符的文本值。
这是 lex 文件:
%{
%}
%%
"&&" {return OP_AND;}
\"|"\"|" {return OP_OR;}
!= {return OP_NE;}
== {return OP_EQ;}
> {return OP_GT;}
>= {return OP_GE;}
\< {return OP_LT;}
\<= {return OP_LE;}
! {return OP_NOT;}
= {return ASSIGN;}
\+ {return PLUS;}
\- {return MINUS;}
\* {return MULT;}
\/ {return DIV;}
\& {return ADRS;}
\^ {return PTR_VAL;}
\; {return SEMI;}
, {return COMMA;}
\{ {return CURLY_O;}
\} {return CURLY_C;}
\( {return PAREN_O;}
\) {return PAREN_C;}
\| {return BAR;}
\[ {return SQR_O;}
\] {return SQR_C;}
else {return ELSE;}
if {return IF;}
do {return DO;}
for {return FOR;}
bool {return BOOL;}
void {return VOID;}
int { return INT;}
charp {return CHARP;}
intp {return INTP;}
string {return STRING;}
while {return WHILE;}
true {return TRUE_;}
false {return FALSE_;}
return {return RETURN;}
null {return NULL_;}
0|[1-9][0-9]* {return CONST_INT;}
[a-zA-Z][a-zA-Z0-9]* { return IDENT;}
\'.\' {return CONST_CHAR; }
\".*\" {return CONST_STRING; }
\[$([^$]*$+[^\]])*[^$]*$+\] { /*comment*/}
[ \n\t] { /* skip whitespace */}
. {return ILLEAGAL;}
%%
这是 yacc 文件: * 请注意,有些规则尚未完全准备好,但未被此输入文件使用,与本案例无关。
%{
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
extern char * yytext;
typedef struct node
{
char * token;
struct node *b1;
struct node *b2;
struct node *b3;
struct node *b4;
}node;
int yylex();
int yyerror();
struct node* mknode2(char* token,node* b1,node* b2);
struct node* mknode3(char* token,node*b1,node*b2,node*b3);
struct node* mknode4(char* token,node*b1,node*b2,node*b3,node*b4);
int printtree(node* tree);
#define YYSTYPE node*
%}
%token SEMI COMMA CURLY_O CURLY_C PAREN_O PAREN_C SQR_O SQR_C BAR
%token BOOL INT CHAR STRING INTP CHARP VOID
%token IF DO WHILE FOR RETURN
%token CONST_INT CONST_CHAR CONST_STRING NULL_ IDENT TRUE_ FALSE_
%token ILLEAGAL
%right ASSIGN
%left OP_OR OP_AND OP_NE OP_EQ OP_GT OP_GE OP_LT OP_LE
%left PLUS MINUS
%left MULT DIV
%right OP_NOT
%right ADRS
%right PTR_VAL
%nonassoc LOWER_THAN_ELSE
%nonassoc ELSE
%start program
%%
program : functions {printtree();}
;
functions : functions function {$$ = mknode2("FUNC" ,,);}
| {$$ = NULL;}
;
function : type id PAREN_O f_params PAREN_C CURLY_O body CURLY_C { $$ = mknode3(,,,);}
;
body : var_decls stmts ret_stmt {$$ = mknode3("BODY" , , , );}
;
nest_block : stmts {$$=;}
;
f_params : f_params_ {$$ = ;}
| {$$ = NULL;}
;
f_params_ : f_params_ COMMA param {$$ = mknode2("," , , );}
| param {$$ = ;}
;
param : type id {$$ = mknode2( , NULL , );}
;
var_decls : var_decls var_decl { $$ = mknode2("DECL" , , );}
| var_decls function {$$ = mknode2("DECL" , , );}
| {$$ = NULL;}
;
var_decl : type var_list SEMI { $$ = mknode2( , NULL , ); }
;
var_list : var_list COMMA dec_assign {$$ = mknode2(",", , );}
| dec_assign { $$ = ;}
;
dec_assign : id ASSIGN expr { $$ = mknode2("=", , );}
| id {$$ = ; }
;
type : BOOL {$$ = yytexy;}
| INT { $$ = yytext;}
| CHAR {$$ = yytext;}
| INTP {$$ = yytext;}
| CHARP {$$ = yytext;}
| STRING {$$ = yytext;}
| VOID {$$ = yytext;}
;
stmts : stmts stmt { $$ = mknode2("STMT" , , );}
| { $$ = NULL ;}
;
stmt : assignment SEMI {$$ = ;}
| fct_call SEMI {$$ = ; }
| IF PAREN_O expr PAREN_C opt_nest %prec LOWER_THAN_ELSE {$$ = mknode2("if" , , );}
| IF PAREN_O expr PAREN_C opt_nest ELSE opt_nest {$$ = mknode3("if" , , , );}
| WHILE PAREN_O expr PAREN_C opt_nest {$$ = mknode2("while" , , );}
| DO CURLY_O nest_block CURLY_C WHILE PAREN_O expr PAREN_C SEMI {$$ = mknode2("do" , , );}
| FOR PAREN_O assignment SEMI expr SEMI assignment PAREN_C opt_nest {$$ = mknode4("for" , , , , );}
;
opt_nest : CURLY_O nest_block CURLY_C {$$ = ;}
| stmt {$$ = ;}
;
ret_stmt : RETURN expr SEMI { $$ = mknode2("return" , NULL , );}
| { $$ = NULL; }
;
assignment : id ASSIGN expr {$$ = mknode2("=" , , );}
| id SQR_O expr SQR_C ASSIGN expr {$$ = mknode3("=" , , , );}
;
fct_call : id PAREN_O expr_list PAREN_C {$$ = mknode2("FUNCALL" , , );}
;
expr_list : expr_list_ expr {$$ = mknode2("AGRS" , , );}
| {$$ = NULL;}
;
expr_list_ : expr_list_ expr COMMA { $$ = mknode2("," , , );}
| {$$ = NULL;}
;
id : IDENT { $$ = mknode2(strdup(yytext) , NULL , NULL);}
;
expr : expr PLUS expr { $$ = mknode2("+" , , );}
| expr MINUS expr { $$ = mknode2("-" , , );}
| expr MULT expr { $$ = mknode2("*" , , );}
| expr DIV expr { $$ = mknode2("/" , , );}
| expr OP_AND expr { $$ = mknode2("&&" , , );}
| expr OP_OR expr { $$ = mknode2("||" , , );}
| expr OP_NE expr { $$ = mknode2("!=" , , );}
| expr OP_EQ expr {$$=mknode2("==",,);}
| expr OP_GT expr {$$=mknode2(">",,);}
| expr OP_GE expr {$$=mknode2(">=",,);}
| expr OP_LT expr {$$=mknode2("<",,);}
| expr OP_LE expr {$$=mknode2("<=",,);}
| OP_NOT expr {$$=mknode2("!",NULL,);}
| PTR_VAL expr {$$=mknode2("^",NULL,);}
| ADRS expr {$$=mknode2("&",NULL,);}
| MINUS expr {$$=mknode2("-",NULL,);}
| literal {$$=mknode2(,NULL,NULL);}
| fct_call {$$= ;}
| id {$$= ;}
| PAREN_O expr PAREN_C {$$=mknode2("(_)",NULL,);}
| BAR expr BAR {$$=mknode2("|_|",NULL,);}
;
literal : CONST_INT {$$ = "const_int";}
| TRUE_ { $$ = "true" ;}
| FALSE_ { $$ = "false" ;}
| CONST_CHAR { $$ = "const_char" ;}
| CONST_STRING { $$ = "const_string" ;}
| NULL_ { $$ = "null" ;}
;
%%
#include "lex.yy.c"
void main(){
return yyparse();
}
int tabCount =0;
struct node* mknode2(char* token,node*b1,node*b2)
{
node* newnode=(node*)malloc(sizeof(node));
char* newstr;
if( token ){
printf("token: %s \n" , token);
newstr=(char*)malloc(sizeof(token)+1);
newstr[sizeof(token)]='[=11=]';
strcpy(newstr,token);
}
else{
newstr=(char*)malloc(3);
strcpy(newstr,"AA");
}
newnode->b1=b1;
newnode->b2=b2;
newnode->b3=NULL;
newnode->b4=NULL;
newnode->token=newstr;
return newnode;
}
struct node* mknode3(char* token,node*b1,node*b2,node*b3)
{
node* newnode= mknode2(token,b1,b2);
newnode->b3=b3;
return newnode;
}
struct node* mknode4(char* token,node*b1,node*b2,node*b3,node*b4)
{
node* newnode= mknode3(token,b1,b2,b3);
newnode->b4=b4;
return newnode;
}
void printTabs(){
int i;
for(i=0;i<tabCount;i++){
printf("\t");
}
}
int printtree(node* tree)
{
tabCount++;
printTabs();
printf("%s\n",tree->token);
if(tree->b1)printtree(tree->b1);
if(tree->b2)printtree(tree->b2);
if(tree->b3)printtree(tree->b3);
if(tree->b4)printtree(tree->b4);
tabCount--;
return 1;
}
int yyerror(){
printf("ERROR!\n");
return 0;
}
这是输入文件:
void main(int x){
int y,w,r; int z;
}
这是预先排序的输出 AST:
FUNC
void
main
int x
x
BODY
DECL
int z;
;
DECL
int y,w,r;
,
;
,
,
,
我期望的输出:
FUNC
void
main
int
x
BODY
DECL
int
z
DECL
int
,
y
,
w
r
当我得到规则时可以看到:在我的 yacc 文件中键入 yytext 包含整个句子("int y,w,r;" 而不仅仅是 "int")当我得到规则时:id它只包含一个标记,恰好是一个标记向前,然后是预期的标记。
- 请注意,在函数名称和函数参数中,yytext 可以正常工作。
- 我尝试从 lex 文件打印 yytext 并且标记似乎包含正确的值。
yytext
指向最后扫描的词位。之前保存到扫描器内部缓冲区的任何其他指针(例如 yytext
的先前值)都是无效的,并且不会假定指向任何有用的东西。
在解析器操作中,您几乎不知道最后扫描的标记是什么。 Yacc/bison 生成 LALR(1) 解析器,其中 1
表示 "one lookahead token"。这意味着解析器(可以)始终检查 next 标记,如果它这样做,那么 yytext
将指向标记。一些实现总是预读;其他人,如野牛,如果前瞻不会改变解析器行为,有时不会提前阅读。因此,在解析器操作期间 yytext
指向哪个标记 未指定 并且可能会在未来版本或不同的实现中发生变化。
底线:从不在解析器操作中使用yytext
。永远不要将 yytext
的指针值从扫描器传递给解析器。如果您将需要解析器中令牌的字符串值,复制该字符串,然后通过yylval
将副本的地址传递给解析器。如果您使用 strdup
或类似的,不要忘记 free
副本,当您不再需要它时。