yytext накопление проблем в yacc и 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($1);}
           ;

functions  : functions function {$$ = mknode2("FUNC" ,$2,$1);}  
           |    {$$ = NULL;}                    
           ;

function   : type id PAREN_O f_params PAREN_C CURLY_O body CURLY_C { $$ = mknode3($1,$2,$4,$7);}
           ;

body       : var_decls stmts ret_stmt {$$ = mknode3("BODY" ,$1 , $2 , $3);}
           ;

nest_block : stmts {$$=$1;} 
       ;

f_params   : f_params_ {$$ = $1;}    
           | {$$ = NULL;}             
           ;

f_params_  : f_params_ COMMA param {$$ = mknode2("," , $3 , $1);}
           | param {$$ = $1;}
           ;

param      : type id {$$ = mknode2($1 , NULL , $2 );}     
           ;

var_decls  : var_decls var_decl { $$ = mknode2("DECL" , $2 , $1);}
          | var_decls function {$$ = mknode2("DECL" , $2 , $1);}
           | {$$ = NULL;}                       
           ;

var_decl   : type var_list SEMI { $$ = mknode2($1 , NULL , $2); }
           ;

var_list   : var_list COMMA dec_assign {$$ = mknode2(",", $3 , $1);}
           | dec_assign { $$ = $1 ;}
           ;

dec_assign : id ASSIGN expr { $$ = mknode2("=", $1 , $3);}
           | id {$$ = $1; }                      
           ;


type       : BOOL {$$ = yytexy;}        
           | INT { $$ = yytext;}        
           | CHAR {$$ = yytext;}        
           | INTP {$$ = yytext;}        
           | CHARP {$$ = yytext;}       
           | STRING {$$ = yytext;}      
           | VOID {$$ = yytext;}    
           ;



stmts      : stmts stmt { $$ = mknode2("STMT" , $2 , $1 );}     
           | { $$ = NULL ;}         
           ;

stmt       : assignment SEMI {$$ = $1 ;}                
           | fct_call   SEMI    {$$ = $1; }     
           | IF PAREN_O expr PAREN_C opt_nest   %prec LOWER_THAN_ELSE {$$ = mknode2("if" , $3 , $5 );}
           | IF PAREN_O expr PAREN_C opt_nest ELSE opt_nest {$$ = mknode3("if" , $3 ,  $5 , $7 );}          
           | WHILE PAREN_O expr PAREN_C opt_nest {$$ = mknode2("while" , $3 , $5);}
           | DO CURLY_O nest_block CURLY_C WHILE PAREN_O expr PAREN_C SEMI {$$ = mknode2("do" , $3 , $7);}
           | FOR PAREN_O assignment SEMI expr SEMI assignment PAREN_C opt_nest {$$ = mknode4("for" , $3 , $5 , $7 , $9 );}
           ;


opt_nest   : CURLY_O nest_block CURLY_C {$$ = $2;}      
           | stmt {$$ = $1;}    
           ;


ret_stmt   : RETURN expr SEMI { $$ = mknode2("return" , NULL , $2);}
        | { $$ = NULL; }    
           ;

assignment : id ASSIGN expr {$$ = mknode2("=" , $1, $3);}               
          | id SQR_O expr SQR_C ASSIGN expr {$$ = mknode3("=" , $1 , $3, $6 );}     
           ;

fct_call   : id PAREN_O expr_list PAREN_C {$$ = mknode2("FUNCALL" , $1 ,$3 );}
           ;

expr_list  : expr_list_ expr {$$ = mknode2("AGRS" , $2 , $1);}
       | {$$ = NULL;}       
       ;

expr_list_ : expr_list_ expr COMMA { $$ = mknode2("," , $2 , $1);}
       | {$$ = NULL;}       
       ;

id         : IDENT { $$ = mknode2(strdup(yytext) , NULL , NULL);}
           ;



expr       : expr PLUS expr { $$ = mknode2("+" , $1 , $3);} 
           | expr MINUS expr { $$ = mknode2("-" , $1 , $3);}
           | expr MULT expr { $$ = mknode2("*" , $1 , $3);} 
           | expr DIV expr { $$ = mknode2("/" , $1 , $3);}  
           | expr OP_AND expr { $$ = mknode2("&&" , $1 , $3);}  
           | expr OP_OR expr { $$ = mknode2("||" , $1 , $3);}
           | expr OP_NE expr { $$ = mknode2("!=" , $1 , $3);}
           | expr OP_EQ expr {$$=mknode2("==",$1,$3);}
           | expr OP_GT expr {$$=mknode2(">",$1,$3);}   
           | expr OP_GE expr {$$=mknode2(">=",$1,$3);}
           | expr OP_LT expr {$$=mknode2("<",$1,$3);}
           | expr OP_LE expr {$$=mknode2("<=",$1,$3);}
           | OP_NOT expr {$$=mknode2("!",NULL,$2);}
           | PTR_VAL expr {$$=mknode2("^",NULL,$2);}
           | ADRS expr  {$$=mknode2("&",NULL,$2);}
           | MINUS expr {$$=mknode2("-",NULL,$2);}      
           | literal {$$=mknode2($1,NULL,NULL);}
           | fct_call {$$= $1 ;}
           | id {$$= $1;}           
           | PAREN_O expr PAREN_C {$$=mknode2("(_)",NULL,$2);}  
           | BAR expr BAR {$$=mknode2("|_|",NULL,$2);}
           ;

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)]='\0';
        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 работает правильно.
- Я попытался распечатать yytext из файла lex, и токены, похоже, содержат правильные значения.


person RanAB    schedule 05.12.2017    source источник


Ответы (1)


yytext указывает на последнюю просканированную лексему. Любой другой ранее сохраненный указатель на внутренний буфер сканера (например, предыдущее значение yytext) недействителен, и нельзя предполагать, что x указывает на что-либо полезное.

В действии парсера вы действительно очень мало представляете, какой токен был просканирован последним. Yacc / bison генерируют синтаксические анализаторы LALR (1), где 1 указывает «один опережающий токен». Это означает, что синтаксический анализатор (может) всегда проверять следующий токен, и если он это делает, это токен, на который будет указывать yytext. Некоторые реализации всегда читают заранее; другие, такие как bison, иногда не читают вперед, если просмотр вперед не изменяет поведение синтаксического анализатора. Итак, какой токен yytext указывает во время действия синтаксического анализатора, не указан и может измениться в будущих версиях или с другими реализациями.

Итог: Никогда не используйте yytext в действии парсера. И никогда не передавайте значение указателя yytext от сканера к синтаксическому анализатору. Если вам понадобится строковое значение токена в синтаксическом анализаторе, сделайте копию строки и передайте адрес копии синтаксическому анализатору через yylval. Если вы используете strdup или подобное, не забудьте free копию, когда она вам больше не понадобится.

person rici    schedule 05.12.2017