Я пытаюсь реализовать калькулятор гибкости / зубра, который может выполнять арифметические операции с плавающей запятой. Мой гибкий код выглядит так

%{
#include "calc.tab.h"
#include <stdlib.h>

void yyerror(char *s);
%}

digit [0-9]
integer {digit}+
real ({digit}+[.]{digit}*)|({digit}*[.]{digit}+)
exp ({integer}|{real})[eE]-?{integer}

%%

({integer}|{real}|{exp}) { yylval = atof(yytext); return NUMBER; }
[-+*/\n]                 { return *yytext; }
[ \t\v\f\r]              { }
.                        { yyerror("Unknown Character"); }

%%

int yywrap(void)
{
  return 1;
}

И мой код бизона выглядит так

%{
#include <stdio.h>

typedef double YYSTYPE;
#define YYSTYPE_IS_DECLARED

void yyerror(char *s);
extern char *yytext;
extern int yylineno;
%} 

%token NUMBER

%left '+' '-'
%left '*' '/'

%%

program: program expr '\n' { printf("%g\n", $2); }
       | program '\n'
       |
       ;
expr: expr '+' expr { $$ = $1 + $3; }
    | expr '-' expr { $$ = $1 - $3; }
    | expr '*' expr { $$ = $1 * $3; }
    | expr '/' expr { $$ = $1 / $3; }
    | NUMBER { $$ = $1; }
    ;

%%

void yyerror(char *s)
{
  fprintf(stderr, "error: %s at %s, line %d\n", s, yytext, yylineno);
}

int main(int argc, char *argv[])
{
  yyparse();

  return 0;
}

Это не дает правильного результата. Хотя лексический анализатор интерпретирует строки как двойные и правильно сохраняет их в переменной yylval, когда синтаксический анализатор складывает числа, он выдает только 0.0000. Однако, если я объявляю yylval как объединение с помощью директивы %union, состоящей только из одной переменной double lf_val;, и сохраняю вывод atof в этом поле yylval в лексере, а также объявить %token <lf_val> NUMBER и %type <lf_val> expr в синтаксическом анализаторе, все вроде работает.

Но почему не работает простой метод typedef ing YYSTYPE? Я также пробовал #define YYSTYPE double. Это тоже не сработало.

4
borncrusader 22 Янв 2013 в 20:04

1 ответ

Лучший ответ

Что касается %code, в документации Bison говорится:

%code requires [...] is the best place to override Bison's default YYSTYPE
and YYLTYPE definitions.

Так что просто добавьте в верхнюю часть файла bison следующее:

%code requires
  {
    #define YYSTYPE double
  }

Вы также захотите удалить эти две строки:

typedef double YYSTYPE;
#define YYSTYPE_IS_DECLARED

Обратите внимание, что YYSTYPE_IS_DECLARED, насколько мне известно, нигде не задокументирован и, таким образом, предназначен только для внутреннего использования Bison.

Если вы не знакомы с использованием директив Bison %code по сравнению с простыми прологами %{, вы можете найти этот раздел документации интересно прочитать.

5
yroeht 22 Янв 2013 в 21:44
Спасибо @yroeht. Я заметил, что включение #define внутри директивы %code requires { } генерирует определение YYSTYPE в файле .h, который был включен лексером. Таким образом, лексический анализатор выполнял запись в целочисленную переменную, а синтаксический анализатор читал из двойной!
 – 
borncrusader
22 Янв 2013 в 22:48