这是用于为算术表达式生成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';
}
. {}
%%
答案 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
)yylex
,yyerror
和copy_string
main
和yyerror
的原型不正确(或更准确,过时)原型。 &#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()
生成的扫描程序需要main
中flex
的调用,以释放词法分析器分配的资源。特定于野牛的%destructor
可防止语法错误时发生内存泄漏:如果您不使用野牛,则必须找到针对该问题的不同解决方案。
%{
#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);
}
%{
/* 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 --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)