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

Unexpected behavior from lexer and grammar for simple calculator

我要开始探索 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", ); }
        | VARIABLE '=' expr         { sym[] = ; }
        ;
expr:
        INTEGER
        | VARIABLE                  { $$ = sym[]; }
        | expr '+' expr             { $$ =  + ; }
        | expr '-' expr             { $$ =  - ; }
        | expr '*' expr             { $$ =  * ; }
        | expr '/' expr             { $$ =  / ; }
        | '(' expr ')'              { $$ = ; }
        ;
%%
/***************** 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", ); }
       ^
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) 然后是 "syntax error" 消息。我假设消息是在 y.tab.c 的代码中自动生成的。能不能换成更有意义的东西?我是否正确地假设语法错误是因为程序找到了 ) 和换行符以及变量,并且这不符合语法定义的程序语句?

I have clearly included the libraries for use of the printf function.

You included stdio.h in your flex file, but not in your bison file. And the warnings about printf being undeclared are from your bison file, not your flex file.

当您使用 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; ...}

此 flex 模式匹配字符集 [-+()=/*\n] 中的 任意数量 个字符。因此在输入 x=(3+5)\n 中,)\n 被匹配为 单个标记 。但是,操作 returns *yytext,第一个字符 yytext,实际上忽略了 \n。由于您的语法需要 \n,因此会产生语法错误。

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

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

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

%error-verbose

到您的 bison 文件的开头。