3

我正在阅读我在 Internet 上找到的关于 GNU Flex/Bison 的书。这很酷。其中一个示例涉及编写中缀计算器。这很好。

问题是,这本书使用intas YYSTYPE,这会在例如将 1 除以 2 时产生明显的问题。所以我决定修改程序以改为使用float。到目前为止,一切都很好。该程序(源代码如下)编译得很好,但总是给出 0 的答案,无论计算是什么。我也不知道如何调试它,因为它显然是生成的代码。

计算器

 %{
 #include "parser.h"
 %}

 %%

 "+"                        { return ADD; }
 "-"                        { return SUB; }
 "*"                        { return MUL; }
 "/"                        { return DIV; }
 [0-9]+                     { 
                                yylval = atof(yytext); 
                                return NUMBER; 
                            }
 \n                         { return EOL; }
 [ \t]                      { ; }
 .                          { yyerror("Unknown symbol"); }

 %%

钙质

 %{
 #include <stdio.h>

 #define YYSTYPE float
 %}

 %token NUMBER
 %token ADD SUB MUL DIV
 %token EOL

 %%

 /* List of expressions */
 calclist: 
    | calclist AS_result EOL { printf("%f\n-> ", $2); }
    ;

 /* Add/subtract result. Evaluated after multiply/divide result */
 AS_result: MD_result
    | AS_result ADD MD_result       { $$ = $1 + $3; }
    | AS_result SUB MD_result       { $$ = $1 - $3; }
    ;

 /* Multiply/divide result. Evaluated first. */
 MD_result: NUMBER
    | MD_result MUL NUMBER          { $$ = $1 * $3; }
    | MD_result DIV NUMBER          { $$ = $1 / $3; }
    ;
 %%

 int yyerror(char *msg)
 {
    printf("Error: '%s'\n", msg);
    return 0;
 }

 int main(int argc, char **argv)
 {
    printf("-> ");
    yyparse();
    return 0;
 }

生成文件

 make: calc.l calc.y
    bison -Wall -o parser.c --defines=parser.h calc.y
    flex -o scanner.c calc.l
    cc -ggdb -o calc scanner.c parser.c -lfl

 clean:
    rm -f parser.c parser.h scanner.c calc.c calc

示例运行

michael@michael-desktop:~/code/calculator$ ./calc 
-> 1 + 2
0.000000
-> ^C
michael@michael-desktop:~/code/calculator$

对代码的任何部分以及实际问题的反馈表示赞赏。干杯!

4

3 回答 3

3

你是对的,它只适用于除法——奇怪..我没注意到,真丢脸。这是另一件事要尝试:

钙.y:

%{
#include <stdio.h>
%}

%union { double d; }

%token <d> NUMBER
%token ADD SUB MUL DIV
%token EOL

%type <d> MD_result AS_result

%%

calc.l:更改行“yylval = atof(yytext);” 和:

yylval.d = atof(yytext);

现在它说:

-> 1+2
3.000000
-> 2*3
6.000000
-> 4-5
-1.000000
-> 6/4
1.500000

正如预期的那样。

于 2013-03-24T17:16:27.610 回答
2

您的建议以及@Michael 对其进行的更正不起作用,因为#define YYSTYPE它没有传播到扫描仪。该%{ %}块仅用于野牛(好吧,用于生成的解析器),它不会在生成的标头中导出。阅读有关文档%code以了解需要做什么:http ://www.gnu.org/software/bison/manual/bison.html#g_t_0025code-Summary 。在您的情况下,%code requires是合适的:

%code requires
{
# include <stdio.h>

# define YYSTYPE float
# define YYSTYPE_IS_DECLARED
}

一旦为解析器配备了调试操作,就很容易看出出了什么问题:

%debug
%printer { fprintf(yyoutput, "%f", $$); } <>

int main(int argc, char **argv)
{
  printf("-> ");
  yydebug = !!getenv("YYDEBUG");
  yyparse();
  return 0;
}

你会看到的:

Reading a token: -> 1+2
Next token is token NUMBER (0.000000)
Shifting token NUMBER (0.000000)
Entering state 3
Reducing stack by rule 6 (line 28):
   $1 = token NUMBER (0.000000)
-> $$ = nterm MD_result (0.000000)
Stack now 0 1
Entering state 5
Reading a token: Next token is token ADD (0.000000)
Reducing stack by rule 3 (line 22):
   $1 = nterm MD_result (0.000000)
-> $$ = nterm AS_result (0.000000)
Stack now 0 1
Entering state 4
Next token is token ADD (0.000000)
Shifting token ADD (0.000000)
Entering state 6
Reading a token: Next token is token NUMBER (0.000000)
Shifting token NUMBER (0.000000)
Entering state 3
Reducing stack by rule 6 (line 28):
   $1 = token NUMBER (0.000000)
-> $$ = nterm MD_result (0.000000)
Stack now 0 1 4 6
Entering state 11
Reading a token: Next token is token EOL (0.000000)
Reducing stack by rule 4 (line 23):
   $1 = nterm AS_result (0.000000)
   $2 = token ADD (0.000000)
   $3 = nterm MD_result (0.000000)
-> $$ = nterm AS_result (0.000000)

这表明当我输入“1+2”时,它实际上到处都能看到 0.00000。

现在,它是如何对除法“起作用”的?偶然,因为浮点除法和整数除法在位级别非常相似。玩弄以下重复错误的程序:将变量填充为整数,但将它们用作浮点数。

#include <stdio.h>

union YYSTYPE
{
  int ival;
  float fval;
};

#define TRY(L, O, R)                            \
  do {                                          \
    union YYSTYPE lhs, rhs, res;                \
    lhs.ival = L;                               \
    rhs.ival = R;                               \
    res.fval = lhs.fval O rhs.fval;             \
    fprintf (stdout, "d. %d %s %d => %f\n",     \
             lhs.ival, #O, rhs.ival, res.fval); \
    fprintf (stdout, "x. %x %s %x => %x\n",     \
             lhs.ival, #O, rhs.ival, res.ival); \
  } while (0)

int main()
{
  TRY(1, /, 2);
  TRY(1, *, 2);
  TRY(1, -, 2);
  TRY(1, +, 2);
  return 0;
}

但是,当然,正确的答案不是使用#define YYSTYPE,这太粗粒度了,而是使用%union,正如@Michael 建议的那样。

于 2013-03-25T06:46:13.943 回答
2

在 calc.y 中:添加第二个定义:

 %{
 #include <stdio.h>

 #define YYSTYPE float
 #define YYSTYPE_IS_DECLARED
 %}

接着..

./calc 
-> 1/3
0.333333
于 2013-03-24T15:36:16.657 回答