3

インターネットで見つけたGNUFlex/Bisonに関する本を読んでいます。そして、それはかなりクールです。例の1つは、中置計算機の作成です。そして、それはうまくいきます。

問題は、この本がintasYYSTYPEを使用していることです。これは、たとえば1を2で割ると明らかな問題を引き起こします。そこで、float代わりに使用するようにプログラムを変更することにしました。ここまでは順調ですね。プログラム(ソースは以下に示されています)は正常にコンパイルされますが、計算が何であれ、常に0の答えを返します。明らかに生成されたコードなので、デバッグする方法もわかりません。

calc.l

 %{
 #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"); }

 %%

calc.y

 %{
 #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;
 }

Makefile

 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

そうです、それは除算のためだけに機能します-奇妙な..私は気づいていません、私を恥じています。試すべき別のことがあります:

calc.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

calc.yで:2番目の定義を追加します:

 %{
 #include <stdio.h>

 #define YYSTYPE float
 #define YYSTYPE_IS_DECLARED
 %}

その後..

./calc 
-> 1/3
0.333333
于 2013-03-24T15:36:16.657 に答える
2

#define YYSTYPEがスキャナーに伝達されないため、提案と@Michaelによる修正は機能しません。ブロックはバイソン専用であり(%{ %}生成されたパーサー用)、生成されたヘッダーにはエクスポートされません。%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が表示されることを示しています。

さて、なぜそれは除算のために「機能する」のでしょうか?偶然ですが、float除算とint除算はビットレベルで非常に似ているためです。間違いを繰り返す次のプログラムを試してみてください。変数を整数として入力しますが、浮動小数点数として使用します。

#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;
}

しかし、もちろん、正しい答えは、あまりにも粗い粒子を使用するのではなく、@Michaelが提案したようにを使用することです。#define YYSTYPE%union

于 2013-03-25T06:46:13.943 に答える