来自词法分析器和语法的简单计算器的意外行为

时间:2015-10-22 10:12:54

标签: c bison flex-lexer

我开始研究Flex和Bison的整个世界。所以我按照教程为flex写了这个l文件:

%{
   #include <stdio.h>
   #include <stdlib.h>
   void yyerror(char *);
   #include "y.tab.h"
%}

%%

 /******************** RULES ********************/

 /* One letter variables */
[a-z]         {
         yylval = *yytext - 'a'; // This is to return a number between 0 and 26 representting the letter variable.
         printf("VAR: %s\n",yytext);
         return VARIABLE;
              }

 /* Integer constants */
[0-9]+        {
         yylval = atoi(yytext);
         printf("INT: %d\n",yylval);
         return INTEGER;
              }

 /* Operators */
[-+()=/*\n]+  { printf("OPR: %s\n",yytext); return *yytext; /*\n is considered an operator because it signals the end of a statement*/ }

 /* This skips white space and tab chararcters */
[ \t]         ;


 /* Anything esle is not allowed */
.             yyerror("Invalid character found");

 /***************** SUBROUTINES *****************/
%%

int yywrap(void){
   return 1;
}

这就是语法:

/***************** DEFINITIONS *****************/

%token INTEGER VARIABLE
%left '+' '-'
%left '*' '/'

%{
   void yyerror(char *);
   int yylex(void);
   int sym[26];
%}

%%
/******************** RULES ********************/

program:
        program statement '\n'
        |
        ;

statement:
        expr                        { printf("EXPR: %d\n", $1); }
        | VARIABLE '=' expr         { sym[$1] = $3; }
        ;
expr:
        INTEGER
        | VARIABLE                  { $$ = sym[$1]; }
        | expr '+' expr             { $$ = $1 + $3; }
        | expr '-' expr             { $$ = $1 - $3; }
        | expr '*' expr             { $$ = $1 * $3; }
        | expr '/' expr             { $$ = $1 / $3; }
        | '(' expr ')'              { $$ = $2; }
        ;
%%
/***************** SUBROUTINES *****************/
void yyerror(char *s){
   printf("%s\n",s);
}

int main(void) {
   yyparse();
   return 0;
}

出现了几个问题。第一个来自编译时。这是我编译的方式:

bison -d bas.y -o y.tab.c
flex bas.l
gcc y.tab.h lex.yy.c  y.tab.c -o bas_fe

这给了我两个这样的警告:

bas.y:24:7: warning: incompatible implicit declaration of built-in function ‘printf’
         expr                        { printf("EXPR: %d\n", $1); }
       ^
bas.y: In function ‘yyerror’:
bas.y:39:4: warning: incompatible implicit declaration of built-in function ‘printf’
    printf("%s\n",s);

现在,它们是警告和打印工作,但我发现它很奇怪,因为我已经清楚地包含了使用printf函数的库。

我真正的问题来自于我与该计划的互动。这是控制台输出:

x = (3+5)
VAR: x
OPR: =
OPR: (
INT: 3
OPR: +
INT: 5
x
OPR: )

VAR: x
syntax error

由此产生了几个问题。 1)输入x =(3 + 5)后,程序打印输出不包括')'为什么?

2)当输入x(预期输出为8)时,才会出现')'。为什么?

3)然后出现“语法错误”消息。我假设消息是在y.tab.c的代码中自动生成的。可以改成更有意义的东西吗?我是正确的,假设语法错误是因为程序找到了)和换行符和变量,并且这不符合程序语句,如语法所定义的那样?

1 个答案:

答案 0 :(得分:0)

  

我已清楚地包含了使用printf函数的库。

     

您在Flex文件中包含了stdio.h,但在您的bison文件中没有。关于printf未声明的警告来自你的野牛文件,而不是你的flex文件。

使用gcc(或任何其他C编译器)编译多个文件时,文件将独立编译,然后链接在一起。所以你的命令

gcc y.tab.h lex.yy.c  y.tab.c -o bas_fe

不会连接这三个文件并将它们编译为一个单元。相反,它独立编译三个文件,包括无用地编译头文件y.tab.h

您应该做的是在您的#include <stdio.h>文件中添加包含bas.y的序言块。

[-+()=/*\n]+ {... return *yytext; ...}

此弹性模式匹配集合[-+()=/*\n]任意数量的字符。因此,在输入x=(3+5)\n中,)\n被匹配为单个令牌。但是,该操作会返回*yytext yytext的第一个字符,有效地忽略\n。由于您的语法需要\n,因此会产生语法错误。

只需从模式中删除重复运算符即可。

可以将错误消息更改为更有意义的内容吗?

如果您有一个相当现代的野牛,请添加声明

%error-verbose

到你的野牛文件的开头。