Yacc 解析不会完成减少生产
Yacc parse won't finish reduction of a production
我正在通过 lax 和 yacc 构建一个小型解释器,该解释器使用一种基本的编程语言来执行加法和乘法以及打印整数列表。
例如指令:
Print(2,3,4);
应该输出:2 3 4
和说明:
Print(+(2,3));
应该输出:5
第一个打印指令工作得很好。然而,任何加法指令(+ 后跟一个列表)都会自行解决并且操作 returns 正确答案(通过 printf 找到)但似乎 yacc 在加法指令之后执行外部打印指令之前停止。
这是我的 .l 文件:
%{
#include "y.tab.h"
%}
digit [0-9]
%%
{digit}{digit}* {yylval.str = strdup(yytext); return IntLit;}
Print {return Print;}
\+ {yylval.str = strdup(yytext); return '+';}
\* {yylval.str = strdup(yytext); return '*';}
\( {return '(';}
\) {return ')';}
\, {return ',';}
\; {return ';';}
\t {}
\r {}
\n {}
%%
int yywrap () {
return 1;
}
这是我的 .y 文件:
%{
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
extern int yylex();
extern int yyparse();
extern int yyerror(char *s);
extern char *yytext;
void doPrint(char *s);
int evaluate(char *c, char *s);
char* append(char *s, char *s2);
char* makeSingle(char *s);
%}
%union {
char character;
char *str;
}
%type <str> Item
%type <str> IntLit
%type <str> List
%type <str> Func
%token Print
%token IntLit
%%
Prog : StmtSeq { };
StmtSeq : Stmt StmtSeq { };
StmtSeq : { };
Stmt : Print '(' List ')' ';' { doPrint(); };
List : List ',' Item { $$ = append(, ); };
List : Item { $$ = makeSingle(); };
Item : Func '(' List ')' { $$ = evaluate(, ); };
Item : IntLit { $$ = ;};
Func : '+' {$$ = yylval.str; };
Func : '*' {$$ = yylval.str; };
%%
int main(int argc, char *argv[]){
//yydebug = 1;
return yyparse();
}
void doPrint(char *s){
char * token = strtok(s, ",");
while(token != NULL){
printf("%s", token);
printf(" ");
token = strtok(NULL, ",");
}
printf("\n");
}
int evaluate(char *c, char *s){
char * result;
int res;
int x;
char * token;
int cmp = strcmp(c, "+");
if(cmp == 0){
token = strtok(s, ",");
res = 0;
while(token != NULL){
x = atoi(token);
res = res + x;
token = strtok(NULL, ",");
}
sprintf(result,"%d",res);
} else {
token = strtok(s, ",");
res = 1;
while(token != NULL){
x = atoi(token);
res = res * x;
token = strtok(NULL, ",");
}
sprintf(result,"%d",res);
}
printf("resultstring is: '%s'\n", result);
return result;
}
char* append(char *s, char *s2){
char * result = s;
strcat(result, ",");
strcat(result, s2);
return result;
}
char* makeSingle(char *s){
//char * result;
//sprintf(result, "%d", c);
//return result;
return s;
}
extern int yyerror(char *s) {
printf(s);
return 1;
}
如果我输入指令:
Print(2,3,4);
Print(+(2,3));
第一条打印指令按预期工作,但第二条指令在打印加法结果之前停止,但在评估完成之后。
我是 yacc/lex 的新手,我不确定为什么 yacc 在不打印加法结果的情况下停止。添加的结果不应该是一个可以重写为“列表”然后正确打印的“项目”吗?任何帮助或建议将不胜感激,谢谢!
编辑
在查看 yydebug = 1;
时的输出后,我发现解析在完全减少之前突然结束(我相信)。调试过程的最后一节内容如下:
Reducing stack by rule 7 (line 40):
= nterm Func ()
= token '(' ()
= nterm List ()
= token ')' ()
本次减少中没有 $$
,因为之前所有其他减少中都有
当你这样做时你会看到什么:doPrint(1);
?
您在 doPrint 中有一行:char * token = strtok(s, ",");
当你通过评估函数时,你会消耗掉所有的逗号,只留下一个数字。当您调用 doPrint 时,字符串中没有逗号,因此它 returns NULL.
你可以有第二个 if,当它 returns NULL(但字符串有内容)时,或者你可以修改你解析字符串的方式。
顺便说一下,strdup() returns 一个你不释放的 malloced 字符串。
您还使用从 strdup 收到的字符串调用 strcat(),您正在写入内存并且您不知道大小。 strcat() 会让你溢出缓冲区。
更好的方法是,当您将一个字符串附加到另一个字符串并且您不知道它们的大小时,可以这样做:
char *mergestrings(char *str1,char *str2) {
char *newstr=(char*)malloc(sizeof(char)*(strlen(str1)+strlen(str2)+1));
strcpy(newstr,str1);
strcat(newstr,str2);
free(str1);
free(str2);
return newstr;
}
或者使用realloc什么的,但是要注意你对内存的处理。
最大的问题是评估函数的定义和指针的使用。
scanner.l:
%{
#include "../obj/y.tab.h"
%}
digit 0|(([1-9])[0-9]*)
operator [+*]
%%
{digit} {yylval.str = strdup(yytext); return IntLit;}
{operator} {yylval.character=yytext[0]; return Operator;}
"Print" {return Print;}
\( {return LParen;}
\) {return RParen;}
\, {return Comma;}
\; {return SemiCln;}
\t {}
\r {}
\n {}
%%
parser.y:
%{
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
extern int yylex();
extern int yyparse();
extern int yyerror(char *s);
extern char *yytext;
void doPrint(char *s);
char* evaluate(char c, char *s);
char* append(char *s, char *s2);
%}
%union {
char character;
char *str;
}
%type <str> Item List
%type <character> Func
%token Print Operator IntLit LParen RParen SemiCln Comma
%%
Prog : Stmt { }
| Prog Stmt { }
;
Stmt : Print LParen List RParen SemiCln { doPrint(); }
;
List : List Comma Item { $$ = append(, ); }
| Item { $$ = ; }
;
Item : Func LParen List RParen { $$ = evaluate(, ); }
| IntLit { $$ = yylval.str;}
;
Func : Operator { $$ = yylval.character; }
;
%%
int main(int argc, char *argv[]){
return yyparse();
}
void doPrint(char *s){
char *stop = strchr(s, ','), *start=s;
while(stop){
*stop='[=12=]';
printf("%s ", start);
start = stop+1;
stop = strchr(start, ',');
}
printf("%s\n",start);
free(s);
}
char *evaluate(char c, char *s){
int res=0;
if(c == '+'){
char *stop = strchr(s, ','), *start=s;
while(stop){
*stop='[=12=]';
res += atoi(start);
start = stop+1;
stop = strchr(start, ',');
}
res += atoi(start);
} else {
char *stop = strchr(s, ','), *start=s;
res = 1;
while(stop){
*stop = '[=12=]';
res *= atoi(start);
start = stop+1;
stop = strchr(start, ',');
}
res *= atoi(start);
}
free(s);
char *result = (char*)malloc(12); // big enough for a 32 bit integer
sprintf(result,"%d",res);
return result;
}
char* append(char *s, char *s2){
char *result = (char*)realloc(s,strlen(s)+strlen(s2)+2);
if (result) {
strcat(result, ",");
strcat(result, s2);
}
else {
result=(char*)malloc(strlen(s)+strlen(s2)+2);
sprintf(result,"%s,%s",s,s2);
free(s);
}
free(s2);
return result;
}
extern int yyerror(char *s) {
printf(s);
return 1;
}
我正在通过 lax 和 yacc 构建一个小型解释器,该解释器使用一种基本的编程语言来执行加法和乘法以及打印整数列表。
例如指令:
Print(2,3,4);
应该输出:2 3 4
和说明:
Print(+(2,3));
应该输出:5
第一个打印指令工作得很好。然而,任何加法指令(+ 后跟一个列表)都会自行解决并且操作 returns 正确答案(通过 printf 找到)但似乎 yacc 在加法指令之后执行外部打印指令之前停止。
这是我的 .l 文件:
%{
#include "y.tab.h"
%}
digit [0-9]
%%
{digit}{digit}* {yylval.str = strdup(yytext); return IntLit;}
Print {return Print;}
\+ {yylval.str = strdup(yytext); return '+';}
\* {yylval.str = strdup(yytext); return '*';}
\( {return '(';}
\) {return ')';}
\, {return ',';}
\; {return ';';}
\t {}
\r {}
\n {}
%%
int yywrap () {
return 1;
}
这是我的 .y 文件:
%{
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
extern int yylex();
extern int yyparse();
extern int yyerror(char *s);
extern char *yytext;
void doPrint(char *s);
int evaluate(char *c, char *s);
char* append(char *s, char *s2);
char* makeSingle(char *s);
%}
%union {
char character;
char *str;
}
%type <str> Item
%type <str> IntLit
%type <str> List
%type <str> Func
%token Print
%token IntLit
%%
Prog : StmtSeq { };
StmtSeq : Stmt StmtSeq { };
StmtSeq : { };
Stmt : Print '(' List ')' ';' { doPrint(); };
List : List ',' Item { $$ = append(, ); };
List : Item { $$ = makeSingle(); };
Item : Func '(' List ')' { $$ = evaluate(, ); };
Item : IntLit { $$ = ;};
Func : '+' {$$ = yylval.str; };
Func : '*' {$$ = yylval.str; };
%%
int main(int argc, char *argv[]){
//yydebug = 1;
return yyparse();
}
void doPrint(char *s){
char * token = strtok(s, ",");
while(token != NULL){
printf("%s", token);
printf(" ");
token = strtok(NULL, ",");
}
printf("\n");
}
int evaluate(char *c, char *s){
char * result;
int res;
int x;
char * token;
int cmp = strcmp(c, "+");
if(cmp == 0){
token = strtok(s, ",");
res = 0;
while(token != NULL){
x = atoi(token);
res = res + x;
token = strtok(NULL, ",");
}
sprintf(result,"%d",res);
} else {
token = strtok(s, ",");
res = 1;
while(token != NULL){
x = atoi(token);
res = res * x;
token = strtok(NULL, ",");
}
sprintf(result,"%d",res);
}
printf("resultstring is: '%s'\n", result);
return result;
}
char* append(char *s, char *s2){
char * result = s;
strcat(result, ",");
strcat(result, s2);
return result;
}
char* makeSingle(char *s){
//char * result;
//sprintf(result, "%d", c);
//return result;
return s;
}
extern int yyerror(char *s) {
printf(s);
return 1;
}
如果我输入指令:
Print(2,3,4);
Print(+(2,3));
第一条打印指令按预期工作,但第二条指令在打印加法结果之前停止,但在评估完成之后。
我是 yacc/lex 的新手,我不确定为什么 yacc 在不打印加法结果的情况下停止。添加的结果不应该是一个可以重写为“列表”然后正确打印的“项目”吗?任何帮助或建议将不胜感激,谢谢!
编辑
在查看 yydebug = 1;
时的输出后,我发现解析在完全减少之前突然结束(我相信)。调试过程的最后一节内容如下:
Reducing stack by rule 7 (line 40):
= nterm Func ()
= token '(' ()
= nterm List ()
= token ')' ()
本次减少中没有 $$
,因为之前所有其他减少中都有
当你这样做时你会看到什么:doPrint(1);
?
您在 doPrint 中有一行:char * token = strtok(s, ",");
当你通过评估函数时,你会消耗掉所有的逗号,只留下一个数字。当您调用 doPrint 时,字符串中没有逗号,因此它 returns NULL.
你可以有第二个 if,当它 returns NULL(但字符串有内容)时,或者你可以修改你解析字符串的方式。
顺便说一下,strdup() returns 一个你不释放的 malloced 字符串。
您还使用从 strdup 收到的字符串调用 strcat(),您正在写入内存并且您不知道大小。 strcat() 会让你溢出缓冲区。
更好的方法是,当您将一个字符串附加到另一个字符串并且您不知道它们的大小时,可以这样做:
char *mergestrings(char *str1,char *str2) {
char *newstr=(char*)malloc(sizeof(char)*(strlen(str1)+strlen(str2)+1));
strcpy(newstr,str1);
strcat(newstr,str2);
free(str1);
free(str2);
return newstr;
}
或者使用realloc什么的,但是要注意你对内存的处理。
最大的问题是评估函数的定义和指针的使用。
scanner.l:
%{
#include "../obj/y.tab.h"
%}
digit 0|(([1-9])[0-9]*)
operator [+*]
%%
{digit} {yylval.str = strdup(yytext); return IntLit;}
{operator} {yylval.character=yytext[0]; return Operator;}
"Print" {return Print;}
\( {return LParen;}
\) {return RParen;}
\, {return Comma;}
\; {return SemiCln;}
\t {}
\r {}
\n {}
%%
parser.y:
%{
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
extern int yylex();
extern int yyparse();
extern int yyerror(char *s);
extern char *yytext;
void doPrint(char *s);
char* evaluate(char c, char *s);
char* append(char *s, char *s2);
%}
%union {
char character;
char *str;
}
%type <str> Item List
%type <character> Func
%token Print Operator IntLit LParen RParen SemiCln Comma
%%
Prog : Stmt { }
| Prog Stmt { }
;
Stmt : Print LParen List RParen SemiCln { doPrint(); }
;
List : List Comma Item { $$ = append(, ); }
| Item { $$ = ; }
;
Item : Func LParen List RParen { $$ = evaluate(, ); }
| IntLit { $$ = yylval.str;}
;
Func : Operator { $$ = yylval.character; }
;
%%
int main(int argc, char *argv[]){
return yyparse();
}
void doPrint(char *s){
char *stop = strchr(s, ','), *start=s;
while(stop){
*stop='[=12=]';
printf("%s ", start);
start = stop+1;
stop = strchr(start, ',');
}
printf("%s\n",start);
free(s);
}
char *evaluate(char c, char *s){
int res=0;
if(c == '+'){
char *stop = strchr(s, ','), *start=s;
while(stop){
*stop='[=12=]';
res += atoi(start);
start = stop+1;
stop = strchr(start, ',');
}
res += atoi(start);
} else {
char *stop = strchr(s, ','), *start=s;
res = 1;
while(stop){
*stop = '[=12=]';
res *= atoi(start);
start = stop+1;
stop = strchr(start, ',');
}
res *= atoi(start);
}
free(s);
char *result = (char*)malloc(12); // big enough for a 32 bit integer
sprintf(result,"%d",res);
return result;
}
char* append(char *s, char *s2){
char *result = (char*)realloc(s,strlen(s)+strlen(s2)+2);
if (result) {
strcat(result, ",");
strcat(result, s2);
}
else {
result=(char*)malloc(strlen(s)+strlen(s2)+2);
sprintf(result,"%s,%s",s,s2);
free(s);
}
free(s2);
return result;
}
extern int yyerror(char *s) {
printf(s);
return 1;
}