值不会在令牌外的Yacc中分配

时间:2016-01-10 21:04:54

标签: bison yacc lex

在Yacc中,我编写了一个解析器,它应该识别一个数字或字符串,并将其分配给我创建的NAME令牌。似乎工作在于变量被赋值,如果我打印它们的值,它们是正确的。但是,一旦Yacc移动到下一个标记,就会擦除值,就像从未分配变量一样。规则NAME '=' NAME是在规则中分配的地方,但在其他地方没有。将一个NAME /变量的值复制到另一个时,这只是一个问题。在底部是我所指的内容的照片,这是我的代码:

Simple.y

%{
    #include "ch3hdr.h"
    #include <stdio.h>
    int yylex(void);
    void yyerror(char *);
%}

%union {
    char* str;
    double dval;
    struct symtab *symp;
}

%token <symp> NAME
%token <dval> NUMBER
%token <str>  STRING
%left '-' '+'
%left '*' '/'
%nonassoc UMINUS

%type <dval> numexpr
%type <str>  strexpr
%%

statement_list: statement '\n'
    |       statement_list statement '\n'
    ;

statement:  NAME '=' numexpr    { $1->num = $3; $1->type = NUM; }
    |       NAME '=' strexpr    { $1->str = $3; $1->type = STR; }
    |       NAME '=' NAME       { 
                                    $1 = $3;

                                    switch($3->type)
                                    {
                                        case NUM: 
                                        {  
                                            $1->num = $3->num; 
                                            $1->type = NUM; 
                                            break;
                                        }
                                        case STR:
                                        {
                                            $1->str = $3->str;
                                            $1->type = STR;
                                            break;
                                        }
                                        default:  yyerror("Declaration invalid\n"); break;
                                    }
                                }
    |       NAME                { 
                                    switch($1->type)
                                    {
                                        case NUM: fprintf(stderr, "num = %g\n", $1->num); break;
                                        case STR: fprintf(stderr, "str = %s\n", $1->str); break;
                                        default:  fprintf(stderr, "var not defined %g\n", $1->num); break;
                                    }
                                }
    |       numexpr             { fprintf(stderr, "numexpr = %g\n", $1); }
    |       strexpr             { fprintf(stderr, "strexpr = %s\n", $1); }         
    ;

numexpr:    numexpr '+' numexpr   { $$ = $1 + $3; // where $1 is first entered token
                                                        // $3 is next token after "="
                                        }
    |       numexpr '-' numexpr   { $$ = $1 - $3; }
    |       numexpr '*' numexpr   { $$ = $1 * $3; }
    |       numexpr '/' numexpr   
                { 
                    if($3 == 0.0)
                        yyerror("divide by zero");
                    else
                        $$ = $1 / $3;
                }
    |       '-' numexpr %prec UMINUS { $$ = -$2; }
    |       '(' numexpr ')'          { $$ = $2; }
    |       NUMBER                   
    |       NAME '(' numexpr ')' { $$ = ($1->funcptr)($3); }
    ;

strexpr:    STRING 
    ;
%% 

void yyerror(char *str)
{
    fprintf(stderr, "Error %s", str);
}

struct symtab *symlook(char *s) {
    char *p;
    struct symtab *sp;

    for(sp = symtab; sp < &symtab[NSYMS]; sp++) {
        /* is it already here? */
        if(sp->name && !strcmp(sp->name, s))
            return sp;
        if(!sp->name) { /* is it free */
            sp->name = strdup(s);
            return sp; 
        }
        /* otherwise continue to next */
    }
    yyerror("Too many symbols");
    exit(1);    /* cannot continue */
} /* symlook */

void addfunc(char *name, double (*func)())
{
    struct symtab *sp = symlook(name);
    sp->funcptr = func;
}

int main() {
    extern double sqrt(), exp(), log();

    addfunc("sqrt", sqrt);
    addfunc("exp", exp);
    addfunc("log", log);

    yyparse();
    return 0;
}

Simple.l

%{
#include "simple.tab.h"
#include "ch3hdr.h"
#include <math.h>
%}

%%

([0-9]+|([0-9]*\.[0-9]+)([eE][-+]?[0-9]+)?) {
    yylval.dval = atof(yytext); 
    return NUMBER; 
    }

[ \t] ;  /* ignore whitespace */
[A-Za-z][A-Za-z0-9]*   { 
        yylval.symp = symlook(yytext);
        return NAME; 
    }

\"[^"\n]*["\n] { 
        yylval.str = yytext;
        return STRING; 
    }

"$"   return 0; /* logical EOF */
\n    |
.     return yytext[0];
%%

ch3hdr.h

#include <stdlib.h>
#include <string.h>

#define NSYMS 20

typedef enum 
{
    NUM = 1,
    STR = 2
} TYPE;

struct symtab
{
    char *name;
    // ptr to C function to call if this entry is a fucntion name
    double (*funcptr)();
    double value;

    double num;
    char*  str;

    TYPE type;

} symtab[NSYMS];

struct symtab *symlook();

2 个答案:

答案 0 :(得分:2)

问题在于词法分析器规则:

yylval.str = yytext;

这只是将指针复制到将来某个时候会被覆盖的数据。您需要复制数据:

yylval.str = strdup(yytext);

这将造成内存泄漏,除非您在完成它后立即发出相应的free(),有时在解析阶段或之后。

答案 1 :(得分:1)

问题是你在yytext中返回Simple.l,假设它的值保持不变。它不会,因为它是词法分析器的内部值,并且每次调用yylex时都会更新。

我看到来自词法分析器的这个块:

\"[^"\n]*["\n] { 
        yylval.str = yytext;
        return STRING; 
    }

在yacc语法中,使用该值而不复制,例如,

                                    case STR:
                                    {
                                        $1->str = $3->str;
                                        $1->type = STR;
                                        break;
                                    }

偶尔会出现这种情况,但上次我回答时(在 Bison token is rest of the string 中),我发现没有合适的接受答案将此标记为副本。