算术yacc程序如何读取多行输入文件?
How to Read Multiple Lines of input file for arithmetic yacc program?
我是编译器的新手,正在学习制作从 .txt 文件输入多行方程(每行一个方程)的计算器。而我面临的是segmentation fault的问题。
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",);}
Expr : Fact '+' Expr { $$ = + ; }
| Fact '-' Expr { $$ = - ; }
| Fact '*' Expr { $$ = * ; }
| Fact '/' Expr { $$ = / ; }
| Fact { $$ = ; }
| '-' Expr { $$ = -; }
;
Fact : '(' Expr ')' { $$ = ; }
| Id { $$ = ; }
;
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 */
我正在尝试打印表达式和结果。
提前致谢。
在你的解析器中,你有:
Line : Calc {printf("%s",$$); }
现在$$
是规则正在计算的语义值,你还没有给它赋值。所以假设它是未定义的并不是不合理的,这会很糟糕,但事实上它确实有一个值,因为默认规则 $$ = ;
。尽管如此,编写
会更具可读性
printf("%s", );
但这不正确,是吗?毕竟你有
#define YYSTYPE int
所以所有语义类型都是整数。但是你告诉 printf
</code> 是一个字符串 (<code>%s
)。 printf
会相信你,所以它会继续尝试取消引用 int
,就好像它是一个 char*
,结果是可预测的(即段错误)。
您可能正在使用一个足够聪明的编译器来注意到您正在尝试使用 %s
格式代码打印 int
这一事实。但是要么你没有要求编译器帮助你,要么你忽略了它的建议。
始终 编译时启用警告。如果您使用的是 gcc 或 clang,则意味着将 -Wall
放在命令行中。 (如果您使用的是其他编译器,请了解如何产生警告。它将被记录在案。)然后在尝试 运行 程序之前阅读警告并修复它们。
您的代码中还有其他一些错误 and/or 有问题的做法。你的语法不准确(为什么你使用 fact
作为每个运算符的 left-hand 操作数?),尽管你有评论,你的词法扫描器会忽略换行符,所以解析器无法知道是否表达式是每行一个,每行两个,或者分布在多行;这将使计算器难以用作 command-line 工具。
无需定义lex宏digit
; (f)lex 自动识别 Posix 字符 class [[:digit:]]
(以及其他 documented here)。定义宏 num
也不是特别有用。过度使用 lex 宏会使您的程序更难阅读;通常最好直接写出模式:
[[:digit:]]+ { yylval = atoi(yytext); return NUM; }
对于您和任何阅读您的代码的人来说,这将更具可读性并且工作更少。 (如果你的教授或导师不同意,我很乐意直接与他们讨论此事。)
我是编译器的新手,正在学习制作从 .txt 文件输入多行方程(每行一个方程)的计算器。而我面临的是segmentation fault的问题。
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",);}
Expr : Fact '+' Expr { $$ = + ; }
| Fact '-' Expr { $$ = - ; }
| Fact '*' Expr { $$ = * ; }
| Fact '/' Expr { $$ = / ; }
| Fact { $$ = ; }
| '-' Expr { $$ = -; }
;
Fact : '(' Expr ')' { $$ = ; }
| Id { $$ = ; }
;
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 */
我正在尝试打印表达式和结果。 提前致谢。
在你的解析器中,你有:
Line : Calc {printf("%s",$$); }
现在$$
是规则正在计算的语义值,你还没有给它赋值。所以假设它是未定义的并不是不合理的,这会很糟糕,但事实上它确实有一个值,因为默认规则 $$ = ;
。尽管如此,编写
printf("%s", );
但这不正确,是吗?毕竟你有
#define YYSTYPE int
所以所有语义类型都是整数。但是你告诉 printf
</code> 是一个字符串 (<code>%s
)。 printf
会相信你,所以它会继续尝试取消引用 int
,就好像它是一个 char*
,结果是可预测的(即段错误)。
您可能正在使用一个足够聪明的编译器来注意到您正在尝试使用 %s
格式代码打印 int
这一事实。但是要么你没有要求编译器帮助你,要么你忽略了它的建议。
始终 编译时启用警告。如果您使用的是 gcc 或 clang,则意味着将 -Wall
放在命令行中。 (如果您使用的是其他编译器,请了解如何产生警告。它将被记录在案。)然后在尝试 运行 程序之前阅读警告并修复它们。
您的代码中还有其他一些错误 and/or 有问题的做法。你的语法不准确(为什么你使用 fact
作为每个运算符的 left-hand 操作数?),尽管你有评论,你的词法扫描器会忽略换行符,所以解析器无法知道是否表达式是每行一个,每行两个,或者分布在多行;这将使计算器难以用作 command-line 工具。
无需定义lex宏digit
; (f)lex 自动识别 Posix 字符 class [[:digit:]]
(以及其他 documented here)。定义宏 num
也不是特别有用。过度使用 lex 宏会使您的程序更难阅读;通常最好直接写出模式:
[[:digit:]]+ { yylval = atoi(yytext); return NUM; }
对于您和任何阅读您的代码的人来说,这将更具可读性并且工作更少。 (如果你的教授或导师不同意,我很乐意直接与他们讨论此事。)