[cdev] Utility bc

Grigoriy A. Sitkarev sitkarev на komitex.ru
Вс Мар 21 19:10:15 MSK 2010


1. Я думаю что константы, определяющие токены, было лучше определить в 
union а не через #define.

Хотя я не рекомендую злоупотреблять typedef, в случае с константами 
здесь можно было бы определить новый тип:

typedef enum {
	EOF = 0,
	NEWLINE,
	STRING,
	NUMBER,
	...
} token_t;

Здесь нет "зла" потому что мы определяем тип, выводя его напрямую из 
базового типа.

И там где этот тип используется, вы бы писали token_t а не int.

struct keywords {
	char	*name;
	token_t	val;
};

При отладке gdb показывал бы символические имена токенов а не десятичные 
значения.

2. Когда вы инициализируете массив с ключевыми словами, то следовало бы 
делать так:

struct keywords tbl[] = {
{ "auto", Auto },
{ "ibase", Ibase },
	..
{ NULL, 0 }
};

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

Функция get_next_token тогда должна быть объявлена так:

token_t get_next_token();

Токен DEGREE лучше переименовать в CARET.

3. В дизайне есть ошибка. Вы полагаете что ввод может осуществляться 
только с stdin. Если вы внимательно перечтёте мои сообщения, то увидите 
что я упомянул о том что лексическому анализатору назначается откуда 
брать ввод.

Стандартная bc(1) может читать программу не только с stdin но и из файла:

$ cat input.bc
ibase=10
a=4; b=3;
a+b;
quit
$ bc -s -q input.bc
7

Open Group спецификация в первых строках говорит о том что сначала 
интерпретируются файлы, и только потом stdin.

4. Комментарии не обрабатываются. В bc они похожи на комментарии в языке 
Си - начинаются с '/*' и заканчиваются '*/'.

5. В строке 225 вы определяете что далее следует дробная часть числа, 
после точки, но также проверяете не является ли это началом целого 
числа, и получаете его как число с плавающей точкой.

Здесь сразу несколько ошибок:

а) bc внутри работает с десятичной арифметикой, хотя для ввода и вывода 
можно задать разные базы (основания систем счисления) через регистры 
ibase и obase. Числа имеют произвольную точность т.е. разрядность не 
фиксирована.
б) все числа представляются в десятичной форме, вне зависимости от базы, 
и округляются, если невозможно получить точного представления.
в) внутренний регистр scale задаёт количество цифр после десятичной 
точки для некоторых операций и для каждого числа в отдельности. В 
зависимости от операции, используется или scale числа (переменной) или 
операции. Например, при делении у результата цифр десятичных после точки 
будет столько, сколько на момент операции было в регистре scale.
г) Даже если бы вы хотели получить число с плавающей точкой в 
соответствии со спецификацией OpenGroup, вы это делаете не верно. Потому 
что спецификация задаёт его так:

NUMBER  : integer
         | '.' integer
         | integer '.'
         | integer '.' integer
         ;

Если я на вход подам 924.343e+12 по идее ваш лексический анализатор 
должен прочитать это как последовательность:

NUMBER,
VARIABLE,
PLUS,
NUMBER.

Но на самом деле, он у вас его прочтёт как один NUMBER.

6. Проверять символ чтобы потом натравить на него getop() достаточно 
странное решение, но для вас в лоб оно работает. С точки зрения дизайна 
это не правильно. Потому что getop у вас большой switch и он в состоянии 
обнаружить что попался символ "не тот" поэтому сравнение в strchr(3) 
явно лишнее.

7. Дальше у вас ошибка в том что числа для лексического анализатора 
могут быть прочитаны по основанию от 2 до 16 и это есть в спецификации. 
Вы читаете только десятичные числа, что уже не верно.

В функции getf у вас ошибка. Вы к сожалению не понимаете, каким образом 
Си работает с памятью, у вас везде где вы выделяете память через 
malloc(3) ошибки которые приведут к краху вашей программы. Просто вам 
повезло что она работает недолго, но память вы уже корёжите везде, где 
есть malloc(3).

Это самое основное, больше уже просто писать не могу потому что надо 
ответить другим. Вам надо ещё серьёзно поработать.

Василий, вы понимаете что в первую очередь программист это читатель и 
только потом уже писатель? По вашему коду видно что вы что-то понимаете, 
но не всё и вам надо копать глубже и разбираться. Кроме того вам надо 
стать читателем и в обязательном порядке освоить идиомы языка (это 
кстати всех касается, не только вас лично), на котором вы 
программируете. Литературу и проекты которые на мой взгляд стоило бы 
изучить я неоднократно упоминал.

--
Г.А.

Вилежанинов Василий пишет:
> last
> 
> 
> ------------------------------------------------------------------------
> 
> _______________________________________________
> cdev mailing list
> cdev на wiki.syktsu.ru
> http://wiki.syktsu.ru/cgi-bin/mailman/listinfo/cdev





Подробная информация о списке рассылки cdev