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 副本,当您不再需要它时。