Yacc Grammar没有正确缩小输入的最后一个字面值

时间:2016-05-14 21:07:23

标签: grammar yacc lex

这是用于为算术表达式生成3地址代码的代码。

我面临的问题是我的语法能够正确读取输入,直到最后一个字面值。但是无法在' \ n'

之前减少最后一个字面值

示例:x = 1 + 2 * 3 - 4
1,2,3正确读取。即使是减号。当它读取4时,它能够减少2步的语法。然后给出错误。

我已将打印声明用于帮助。这是整个代码。阅读最后一个字面时出现问题:

x -> IDEN | NUM | ....  
f -> x  
t -> f (This doesn't happen)  

icg.y

%{
    #include<stdio.h>
    #include<stdlib.h>
    int temp_reg_num = 0;
    void print_three_address_code(char* text1,char oper,char* text2)
    {
        printf("t%d = %s %c %s\n",temp_reg_num,text1,oper,text2);
    }   
%}

%token IDEN, NUM, FLOAT 
%union {
    char* text;
}

%left '+' '-'
%left '*' '/'

%%
s : IDEN '=' e '\n' {
    printf("start\n");
    if($3.text == NULL)
        printf("%s = t%d\n",$1.text,temp_reg_num-1);
    else
        printf("%s = %s\n",$1.text,$3.text);
    return 0;
}
;
e : e '+' t {
    printf("e -> e + t\n");
    char temp[5];
    print_three_address_code($1.text,'+',$3.text);
    sprintf(temp,"t%d",temp_reg_num++);
    $$.text = copy_string(temp);
}
  | e '-' t {
    printf("e -> e - t\n");
    char temp[5];
    print_three_address_code($1.text,'-',$3.text);
    sprintf(temp,"t%d",temp_reg_num++);
    $$.text = copy_string(temp);
}
  | t {
    printf("e -> t\n");
    $$.text = copy_string($1.text);
}
;
t : t '*' f {
    printf("t -> t * f\n");
    char temp[5];
    print_three_address_code($1.text,'*',$3.text);
    sprintf(temp,"t%d",temp_reg_num++);
    $$.text = copy_string(temp);
}
  | t '/' f {
    printf("t -> t / f\n");
    char temp[5];
    print_three_address_code($1.text,'/',$3.text);
    sprintf(temp,"t%d",temp_reg_num++);
    $$.text = copy_string(temp);
}
  | f {
    printf("t -> f\n");
    $$.text = copy_string($1.text);
}
;
f : f '^' x {
    printf("f -> f ^ x\n");
    char temp[5];
    print_three_address_code($1.text,'^',$3.text);
    sprintf(temp,"t%d",temp_reg_num++);
    $$.text = copy_string(temp);
}
  | x {
    printf("f -> x\n");
    $$.text = copy_string($1.text);
    printf("Why syntax error??");
}
;
x : '(' s ')' {}
      | NUM {
    printf("x -> NUM\n");
    $$.text = copy_string($1.text);
}
      | IDEN {
    printf("x -> IDEN\n");
    $$.text = copy_string($$.text,$1.text);
}
      | FLOAT {
    printf("x -> FLOAT\n");
    $$.text = copy_string($1.text);
}
      | '-' x {
    printf("x -> - x\n");
    $$.text = (char*)malloc(sizeof(char)*(strlen($2.text)+2));
    $$.text[0] = '-';
    strcat($$.text,$2.text);
}
;
%%

main()
{
    printf("Enter your expression : ");
    yyparse();
}
yyerror()
{
    printf("Syntax Error\n");
}
yywrap()
{
    return 1;
}

icg.l

%{
    #include<stdio.h>
    #include<stdlib.h>
    #include"y.tab.h"
    char* copy_string(char* fromstring)  
    {
        char* tostring = (char*)malloc(sizeof(char)*(strlen(fromstring)+1));  
        strcpy(tostring,fromstring);  
        return tostring;  
    }
%}

iden [a-zA-Z_][a-zA-Z_]*  
%%  
[0-9]+ {  
    yylval.text = copy_string(yytext);  
    return NUM;  
}
[0-9]+[.][0-9]+ {  
    yylval.text = copy_string(yytext);  
    return FLOAT;  
}
{iden} {  
    yylval.text = copy_string(yytext);  
    return IDEN;  
}  
[+] {  
    return '+';  
}  
[-] {  
    return '-';  
}  
[*] {  
    return '*';  
}  
[/] {  
    return '/';  
}  
['^'] {  
    return '^';  
}  
[=] {  
    return '=';  
}  
'\n' {  
    return '\n';  
}  
. {}  
%%

1 个答案:

答案 0 :(得分:2)

由于此规则,您收到了语法错误:

'\n' { return '\n'; }

Flex不认为'有任何特殊之处,因此该规则将(仅)匹配三个字符序列&#39; ENTER &#39; 。这不会出现在您的输入中,因此需要\n的规则将不会成功,并且当您提供文件结尾(或文件结束时,如果您正在读取文件) ,那么你会得到一个语法错误。

所以你应该写的是:

\n { return '\n'; }

此外,

['^'] 

不仅匹配^。它也匹配',因为如上所述,'在任何方面都不是特殊的,因此字符类由三个字符组成,其中一个字符重复。要准确匹配字符^,请使用双引号(特殊):

"^"

(但是,"\n"不起作用,除非您要匹配的内容恰好是\后跟n。只需\n即可。)

您实际上可以将所有这些单个字符规则简化为单个规则:

[-+*/^()=\n]   { return yytext[0]; }

我包含了(),虽然它们不在您的icg.l文件中,因为您在语法中使用它们。

但是,您使用它们的规则不正确:

x : '(' s ')' {}

因为s是一项任务。这将允许

x = 1 + (y = 2 * 3) - 4

但不允许更正常的

x = 1 + (2 * 3) - 4

你想要的是:

x : '(' e ')' { $$ = $2; }

您的语法中还有其他各种错误,包括:

  • 缺少#include <string.h>标题(适用于strlen
  • 缺少yylexyyerrorcopy_string
  • 的声明
  • mainyyerror的原型不正确(或更准确,过时)原型。 &#34;现代C&#34; - 在过去30年左右的时间里 - 更喜欢明确的回报类型,因为C99(1999)要求它们。
  • copy_string的实例,带有两个参数而不是一个,这是未定义的行为。 (如果您为copy_string提供声明,则会被标记为错误。)

您的icg.y文件在没有Berkeley yacc(byacc)投诉的情况下处理,但bison即使在yacc兼容模式下也不会处理它。问题是您使用$1.text并且未能声明终端和非终端的标签。如果您要声明union类型,则应为终端提供标记声明:

%token <text> NUM IDEN FLOAT

和您的非终端(或至少是具有语义值的终端):

%type <text> s e t f x

然后,您可以从操作中删除所有.text选择器,因为bison(甚至byacc)将知道要使用哪个选择器。声明令牌标记使您的解析器类型安全,或者至少更少类型不安全,并且通常更具可读性。

最后,您需要开展内存管理工作。你永远不会free()string_copy中分配的任何字符串,所以你逐渐积累了相当多的泄露内存。此外,您可以在许多情况下复制不需要的内容;例如,在单位规则中:

x : NUM { $$.text = copy_string($1.text); }

副本完全没必要,因为$1即将从解析器堆栈弹出,并且它引用的字符串将永远不会再次使用。因此,您只是不必要地泄露副本。更清洁的是

x : NUM { $$ = $1; }

但实际上这是不必要的,因为它是默认操作。

更改单位产品不会阻止其他内存泄漏;在实际上使用语义值而不是通过它们执行某些操作的制作中,如果您不再需要它们,您仍然需要手动free复制的字符串(这似乎是所有语义行为中的情况)。或者,如果以后需要它们,你需要弄清楚如何释放它们。

您可以使用yacc或bison的内置跟踪功能,而不是将您的程序填满printf以后需要删除的程序。只需在运行-t或添加

时提供yacc选项即可
#define YYDEBUG 1

到'%{...%}&#39;阻止你的icg.y文件。然后修改你的主要功能:

int main() {
  yydebug = 1;
  return yyparse();
}

这将告诉您在解析输入时究竟发生了什么,包括从扫描仪接收的终端。

这是一个完整的示例,展示了一种内存管理方法。它基于您的代码,但具有一定数量的重构。请注意,解析器从不复制字符串,但emit_tac确实会分配一个新字符串,并释放它作为参数传递的字符串。 (这可能被一些观察者认为是粗鲁的。)现代yylex_destroy()生成的扫描程序需要mainflex的调用,以释放词法分析器分配的资源。特定于野牛的%destructor可防止语法错误时发生内存泄漏:如果您不使用野牛,则必须找到针对该问题的不同解决方案。

icg.y

%{
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    int yylex();
    int yylex_destroy();
    void yyerror(const char* msg);

    int temp_reg_num = 0;
    char* emit_tac(char* text1, char oper, char* text2) {
        char* rv = malloc(8);
        snprintf(rv, 7, "t%d", temp_reg_num++);
        printf("%s = %s %c %s\n", rv, text1 ? text1 : "", oper, text2);
        free(text1); free(text2);
        return rv;
    }
%}

%union {
    char* text;
}
%token <text> IDEN NUMBER
%type <text> e t f x
%destructor { free($$); } <text>

%%
s : IDEN '=' e '\n' { printf("%s = %s\n", $1, $3);
                      free($1); free($3);
                    }
e : e '+' t         { $$ = emit_tac($1,'+',$3); }
  | e '-' t         { $$ = emit_tac($1,'-',$3); }
  | t
t : t '*' f         { $$ = emit_tac($1,'*',$3); }
  | t '/' f         { $$ = emit_tac($1,'/',$3); }
  | f
f : f '^' x         { $$ = emit_tac($1,'^',$3); }
  | x 
x : IDEN 
  | NUMBER
  | '-' x           { $$ = emit_tac(NULL, '-', $2); }
  | '(' e ')'       { $$ = $2; }
%%

int main() {
    int rc = yyparse();
    yylex_destroy();
    return rc;
}

void yyerror(const char* msg) {
    printf("%s\n", msg);
}

icg.l(需要弹性)

%{
    /* Change to y.tab.h if you use yacc */
    #include "icg.tab.h"
    char* copy_yytext() {
        char* tostring = malloc(yyleng + 1);
        memcpy(tostring, yytext, yyleng);
        tostring[yyleng] = 0;
        return tostring;  
    }
%}
%option noinput nounput noyywrap nodefault

%%

\n                           { return '\n'; }
[[:space:]]                  /* Ignore */
[[:digit:]]+(\.[[:digit]]*)? { yylval.text = copy_yytext(); return NUMBER;  }
[[:alpha:]_][[:alnum:]_]*    { yylval.text = copy_yytext(); return IDEN; } 
.                            { return yytext[0]; }  

编译

$ bison -d icg.y
$ flex icg.l
$ gcc -Wall -o icg lex.yy.c icg.tab.c

使用valgrind进行测试以证明没有内存泄漏

$ valgrind --leak-check=full ./icg <<< ' x = 1 + 2 - 3 / 4 ^ 5 * ( 6 - 9 )'
==26225== Memcheck, a memory error detector
==26225== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==26225== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==26225== Command: ./icg
==26225== 
t0 = 1 + 2
t1 = 4 ^ 5
t2 = 3 / t1
t3 = 6 - 9
t4 = t2 * t3
t5 = t0 - t4
x = t5
==26225== 
==26225== HEAP SUMMARY:
==26225==     in use at exit: 0 bytes in 0 blocks
==26225==   total heap usage: 17 allocs, 17 frees, 16,530 bytes allocated
==26225== 
==26225== All heap blocks were freed -- no leaks are possible
==26225== 
==26225== For counts of detected and suppressed errors, rerun with: -v
==26225== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)