如何为算术yacc程序读取多行输入文件?

时间:2017-02-12 02:01:59

标签: yacc lex

我是编译器的新手并且学习制作计算器,从.txt文件输入多行方程(每行一个方程)。我正面临着细分问题。

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 个答案:

答案 0 :(得分:1)

在您的解析器中,您有:

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

现在$$是规则正在计算的语义值,并且您尚未为其分配任何内容。因此,假设它是未定义的,这将是不合理的,这是不合理的,但实际上由于默认规则$$ = $1;它确实有一个值。尽管如此,编写

会更具可读性
printf("%s", $1);

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

#define YYSTYPE int

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

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

始终编译并启用警告。如果您使用gcc或clang,则意味着将-Wall放在命令行中。 (如果您正在使用其他编译器,请查看如何生成警告。它将被记录。)然后在尝试运行程序之前阅读警告并修复它们。

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

无需定义lex宏digit; (f)lex自动识别Posix角色类[[:digit:]](和其他人,documented here)。定义宏num也不是特别有用。过度使用lex宏会使您的程序更难阅读;通常最好只将模式写出来:

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

对于您和阅读代码的任何人来说,这将更具可读性并且更少工作。 (如果您的教授或导师不同意,我很乐意直接与他们讨论此事。)