2017-02-12 81 views
1

我是编译器新手,学习如何使用计算器从.txt文件输入多行方程(每行一个方程)。而我正面临着分段错误的问题。如何为算术yacc程序读取多行输入文件?

YACC代码:

%{ 
#include <stdio.h> 
#include <string.h> 
#define YYSTYPE int /* the attribute type for Yacc's stack */ 
extern int yylval;  /* defined by lex, holds attrib of cur token */ 
extern char yytext[]; /* defined by lex and holds most recent token */ 
extern FILE * yyin; /* defined by lex; lex reads from this file */ 
%} 

%token NUM 

%% 

Begin : Line   
    | Begin Line  
    ; 
Line : Calc     {printf("%s",$$); } 
    ; 
Calc : Expr     {printf("Result = %d\n",$1);} 
Expr : Fact '+' Expr  { $$ = $1 + $3; } 
    | Fact '-' Expr  { $$ = $1 - $3; } 
    | Fact '*' Expr  { $$ = $1 * $3; } 
    | Fact '/' Expr  { $$ = $1/$3; } 
    | Fact     { $$ = $1;   } 
    | '-' Expr    { $$ = -$2;  } 
    ; 
Fact : '(' Expr ')'   { $$ = $2;   } 
    | Id     { $$ = $1;   } 
    ; 
Id : NUM     { $$ = yylval;  } 
    ; 

%% 

void yyerror(char *mesg); /* this one is required by YACC */ 

main(int argc, char* *argv){ 
char ch; 
if(argc != 2) {printf("useage: calc filename \n"); exit(1);} 
if(!(yyin = fopen(argv[1],"r"))){ 
     printf("cannot open file\n");exit(1); 
} 
yyparse(); 
} 

void yyerror(char *mesg){ 
    printf("Bad Expression : %s\n", mesg); 
    exit(1); /* stop after the first error */ 
} 

LEX代码:

%{ 
#include <stdio.h> 
#include "y.tab.h" 
int yylval; /*declared extern by yacc code. used to pass info to yacc*/ 
%} 

letter [A-Za-z] 
digit [0-9] 
num  ({digit})* 
op  "+"|"*"|"("|")"|"/"|"-" 
ws  [ \t\n] 
other . 

%% 

{ws} { /* note, no return */ } 
{num} { yylval = atoi(yytext); return NUM;} 
{op} { return yytext[0];} 
{other} { printf("bad%cbad%d\n",*yytext,*yytext); return '?'; } 

%% 
/* c functions called in the matching section could go here */ 

我试图与结果一起打印的表达。 提前致谢。

回答

1

在你的解析器,你有:

Line : Calc     {printf("%s",$$); } 

现在$$是语义值的规则计算,而你还没有分配任何东西给它。所以假设它是未定义的并不合理,这将是不好的,但实际上它由于默认规则$$ = $1;而具有价值。所有相同的,它会更可读写

printf("%s", $1); 

但这不正确,是吗?毕竟,你有

#define YYSTYPE int 

所以所有的语义类型都是整数。但是你告诉printf$1是一个字符串(%s)。 printf会相信你,所以它会继续尝试取消引用int,就好像它是char*一样,并带有可预测的结果(即段错误)。

您可能正在使用一种编译器,该编译器足够聪明,可以注意到您正尝试使用%s格式的代码打印int。但是要么你没有要求编译器帮助你,要么你忽略了它的建议。

总是在启用警告的情况下编译。如果您使用的是gcc或clang,那意味着在命令行中输入-Wall。 (如果您正在使用其他编译器,请了解如何生成警告,并将其记录下来。)然后在尝试运行该程序之前阅读警告并修复它们。


你的代码还有其他一些错误和/或可疑的做法。你的语法不准确(你为什么使用fact作为每个操作符的左边操作数?),尽管你的注释中,你的词法扫描器忽略了换行符,所以解析器无法知道表达式是否每行一个,每行两个,或分布在多行;这将使得很难将计算器用作命令行工具。

没有必要定义lex宏digit; (f)lex自动识别Posix字符类[[:digit:]](和其他人,documented here)。定义宏num也不是特别有用。过度使用lex宏使得程序难以阅读;它通常是最好只写模式出来到位:

[[:digit:]]+  { yylval = atoi(yytext); return NUM; } 

这将是更具可读性和较少的工作都为你和任何人阅读你的代码。 (如果你的教授或导师不同意,我很乐意直接与他们讨论这个问题。)

+0

rici我用Expr更新了Fact事物,现在当我编译它时,yacc代码成功了,但是提供了20次转换/减少冲突信息。我能够得到输出但不是.txt中的表达式例如:2 + 3 *( - 7)是在.txt中,但是当我编写文件读取器代码时,它会抛出yyerror消息。 –

+0

@nikul:在回答问题后请不要更改您的问题。 SO旨在成为问题和答案的存储库;当你改变你的问题时,你使答案无效,然后这些信息对其他人没有任何价值。如果答案对您有帮助,您可以为其投票和/或接受;如果没有,你可以降低或忽略它,但无论是哪种情况,如果你还有其他问题,请将它作为另一个问题。 – rici

+0

很抱歉,我是这个网站的新手,我不知道。 –