LINUX.ORG.RU

Работа с Bison. Сложение вещественных чисел.

 ,


0

2

Доброго времени суток. Нужна помощь, ибо сам «в лыжи обутый». Необходимо запрограммировать вычисление синтезируемых атрибутов для программной реализации синтаксически управляемого сложения вещественных чисел.

Т.е. подаем строку 1.01+1.1 Должен быть ответ: 2.11.

Вот грамматика:

S::=E
E::=T+T
T::=L.L
L::=B
L::=LB
B::=0|1|2|3|4|5|6|7|8|9

Понятия не имею, что можно сделать с третьим и пятым правилами. Сначала, довольный жизнью сганашил через математические выражения. Типа так:

S: 	E{$$ = $1; cout << "Output string: " <<$$<<endl;};
E: 	T '+' T {$$=$1+$3;};	
T: 	L '.' L{$$=$1+0.1*$3/10;}; 
L: 	B{$$ = $1; cout<<$$<<endl;};
	|
	L B {$$=$1*10+$2;cout<<$$<<'\n';} ;
B: 	  '0' {$$ = 0;}
	| '1' {$$ = 1;}
	| '2' {$$ = 2;}
Но такой подход не подходит для чисел с разной разрядностью. Потом попробовал все сделать строками, а потом через atoi - тоже не сложилось.

В идеале, как сказал препод, надо бы сделать атрибуты массивами. А в конце запрограммировать сложение в столбик. Буду рад любым идеям. Заранее спасибо.



Последнее исправление: beastie (всего исправлений: 1)

Грамматика эта слишком простая для бизона: бизон предназначен для контекстно-свободных грамматик, а эта --- регулярная, описывается к примеру регулярным выражением [0-9]+ "." [0-9]+ "+" [0-9]+ "." [0-9]+.

Но если уж так надо именно на бизоне (то есть если это учебный пример), то можно просто различать нетерминалы для целой и дробной части:

S: E { cout << "Output string: " << $1 << endl; }

E: T '+' T { $$ = $1 + $3; }

T: H '.' L { $$ = $1 + $3 / 10; }

H   : B   { $$ = $1; }
    | H B { $$ = $1 * 10 + $2; }

L   : B   { $$ = $1; }
    | B L { $$ = $2 / 10 + $1; }

B   : '0' { $$ = 0; }
    | '1' { $$ = 1; }
    | '2' { $$ = 2; }

В дробной части используется правая рекурсия, в целой части --- левая. Это не принципиально (оба случая можно переделать на другую), просто здесь так удобно сворачивать числа.

anonymous
()
Ответ на: комментарий от anonymous

Спасибо, но уже этой тропой ходили. Такой код не примет числа разной размерности, т.е. например 2.2+1.1 он посчитает. А вот 2.01+1.1 он сосчитает неверно. Там, во-первых, при наборе чисел уже в пятом правиле потеряются нули в дробной части, а во-вторых, в правиле с точкой, оно запишет число только с определенной длинной: десятые, сотые.

Gold_Oldmen
() автор топика
Ответ на: комментарий от anonymous

Спасибо!

Извините за предыдущий комментарий. Долбился в глаза, суть не сразу ухватил. Это было отличной идеей преобразовать сами правила. Очень надеюсь, что с таким решением не пошлют. Очень выручили, спасибо!

Gold_Oldmen
() автор топика
Ответ на: Спасибо! от Gold_Oldmen

:)

На всякий случай запощщу пример рабочей программы:

/*
 * $ bison 1.y && gcc 1.tab.c && ./a.out
 * 2.01+1.1
 * Output string: 3.110000
 *
 */

%{
#include <stdlib.h>
#include <stdio.h>

int yylex()
{
    char c = getc(stdin);
    return (c == '\n') ? 0 : c;
}

void yyerror(const char *s)
{
    fprintf(stderr, "%s\n", s);
    exit(1);
}

%}

%union {
        double d;
};

%type <d> S E T H L B

%%

S: E { printf("Output string: %lf\n", $1); }

E: T '+' T { $$ = $1 + $3; }

T: H '.' L { $$ = $1 + $3 / 10; }

H   : B   { $$ = $1; }
    | H B { $$ = $1 * 10 + $2; }

L   : B   { $$ = $1; }
    | B L { $$ = $2 / 10 + $1; }

B   : '0' { $$ = 0; }
    | '1' { $$ = 1; }
    | '2' { $$ = 2; }

%%

int main()
{
    return yyparse();
}

anonymous
()

Парзить числа лучше в лексере, что бы он кушал их в виде -1.234e-15 и выдавал уже эту сроку на гора. (Это именно его задача.) А дальше просто atof.

beastie ★★★★★
()
Последнее исправление: beastie (всего исправлений: 1)
Ответ на: комментарий от beastie

Сказали, низзя atof. Типа, это уже не будет синтаксически управляемым сложением.

Gold_Oldmen
() автор топика
Ответ на: комментарий от anonymous

Спасибо еще раз.

Я запустил с этим:

#ifndef YYSTYPE

#define YYSTYPE double

#endif

вместо union

А чисто теоретически, есть ли метод запилить не преобразуя изначальную грамматику?

Gold_Oldmen
() автор топика
Ответ на: комментарий от Gold_Oldmen

А чисто теоретически, есть ли метод запилить не преобразуя изначальную грамматику?

Да. Нужно всего лишь считать экспоненту (тебе придется поменять тип данных):

%union {
  struct {
    double val;
    int exp;
  } acc;
  double val;
}
%type<acc> L
%type<val> S E T B

/* ... */
T: L '.' L { $$ = $1.val + $2.val * exp10(10, -$2.exp); }

L: B { $$.exp = 1; $$.val = $1; }
 | L B { $$.exp = 1 + $1.exp; $$.val = $1.val * 10 + $2; }

kawaii_neko ★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.