我正在通过 lax 和 yacc 构建一个小型解释器,该解释器使用基本编程语言执行加法和乘法以及打印整数列表。
例如指令:
Print(2,3,4);
应该输出:2 3 4
和指令:
Print(+(2,3));
应该输出:5
第一条打印指令运行良好。然而,任何加法指令(+ 后跟一个列表)都会自行解决,并且该操作会返回正确的答案(通过 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($3); };
List : List ',' Item { $$ = append($1, $3); };
List : Item { $$ = makeSingle($1); };
Item : Func '(' List ')' { $$ = evaluate($1, $3); };
Item : IntLit { $$ = $1;};
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):
$1 = nterm Func ()
$2 = token '(' ()
$3 = nterm List ()
$4 = token ')' ()
在此缩减中没有 $$
,因为之前完成的所有其他缩减中都有
答案 0 :(得分:0)
当你这样做时你看到了什么:doPrint(1);
?
您在 doPrint 中有一行:char * token = strtok(s, ",");
当您通过评估函数时,您会消耗所有逗号,只留下一个数字。当您调用 doPrint 时,字符串中没有逗号,因此返回 NULL。
如果,当它返回 NULL(但字符串有内容)时,您可以有第二个,或者您可以重新处理解析字符串的方式。
顺便说一下,strdup() 返回一个你没有释放的 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 或其他东西,但你应该注意你对内存的处理。
最大的问题是评估函数的定义和指针的使用。
扫描仪.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($3); }
;
List : List Comma Item { $$ = append($1, $3); }
| Item { $$ = $1; }
;
Item : Func LParen List RParen { $$ = evaluate($1, $3); }
| 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='\0';
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='\0';
res += atoi(start);
start = stop+1;
stop = strchr(start, ',');
}
res += atoi(start);
} else {
char *stop = strchr(s, ','), *start=s;
res = 1;
while(stop){
*stop = '\0';
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;
}