Подключение flex и yacc к проекту Qt

HOME
BOOKS
LaTeX
LINKS
NEWS
OTHER

О чем это мы?

Вы пишите программу с использованием Qt и вам понадобилось добавить в нее лексический и/или синтаксический анализатор. Вы решили (или еще не решили) использовать Flex и yacc. Эта статья как раз и рассказывает о том, как подключить анализаторы к проекту Qt. Зачем об этом рассказывать? Да потому что не все очевидно, есть несколько подводных камней, которые хоть и обсуждаются в документации, но совсем не в форме HOWTO, и это обсуждение еще надо найти.

Все ниженаписанное производилось под ОС Gentoo, Qt 4.3.2-r1, flex 2.5.33-r3, bison-2.3 но вполне возможно окажется пригодным и для других версий ПО.

Как работает Flex?

Flex - лексический анализатор. Он читает поток символов и выделяет в нем лексемы (слова некоторого языка), для лексем могут быть определены действия, которые нужно произвести, когда лексема найдена во входном потоке. Отправной точкой для Flex является файл описания лексики, который имеет расширение *.l. О том, как писать эти файлы я рассказывать не буду, это отдельная большая тема. Допустим, у вас есть файл test.l, в котором описана лексика языка и действия для лексем. Теперь нужно скормить этот файл программе Flex командной flex test.l и если все с файлом в порядке, мы получим файл lex.yy.c, который содержит лексический анализатор на C. Потом этот файл компилируется и линкуется с остальными файлами. Вроде бы все просто...

Особенности работы Qt с Flex

Просто? А не тут-то было! Для начала, было бы неплохо, чтобы файлы Flex как-то были добавлены в проект Qt, чтобы не приходилось делать много ручной работы. Это как раз самая очевидная часть интеграции Flex в программу с Qt. Все что нужно сделать - добавить в файл проекта (*.pro) строку вида LEXSOURCES=../../flex_yacc/detect_cfg.l. Теперь Makefile будет содержать инструкции по обработке файла detect_cfg.l, а файл лексического анализатора будет компилироваться и линковаться к проекту.

Добавьте указанным выше образом ваш *.l файл к проекту и попробуйте выполнить make. Вот и первая ошибка: detect_cfg_lex.cpp:(.text+0xa98): undefined reference to `detect_cfgwrap'! Я попробовал спросить Google про функцию detect_cfgwrap и ничего не нашел! Обратите внимание на похожесть имени этой функции и имени файла detect_cfg.l! Оказывается, Qt вызывает flex с параметром -P, поэтому имена функций из yylex() превращаются в detect_cfglex(). Обязательно помните об этом!!! Второй неприятной особенностью является то, что в именах *.l файлов точку можно использовать только перед "расширением" .l, иначе имена функций из yylex() превратятся в test.filelex(), а точка - элемент языка C, и ошибок вы получите предостаточно. Никакого упоминания об этом факте в документации я не нашел. Равно как и о том, что flex вызывается с параметром -P. Так что же делать, чтобы ошибка undefined reference to `somethingwrap' не появлялась? Документация на Flex говорит, что нужно добавить в первую секцию *.l файла инструкцию %option noyywrap.

Если вы еще не пытались вызвать лексический анализатор из своей программы, то, думаю, ошибок при ее сборке не будет. Но если вы добавите вызов анализатора при помощи инструкции detect_cfglex(), попытка сборки приведет к ошибке: main.cpp:26: ошибка: нет декларации ‘detect_cfglex’ в этой области видимости. Решение простое: нужно добавить объявление этой функции extern int detect_cfglex (void);. Посмотреть его можно в сгенерированном Flex файле, который имеет имя *lex.cpp.

Как заставить работать Flex-анализатор с файлом?

Вы сделали лексический анализатор и хотите заставить его анализировать файлы. Документация говорит, что файл, в котором анализатор будет искать лексемы, определяется переменной FILE* yyin. Но, во-первых, имя переменной может быть другим, если мы работаем с Qt. А во-вторых, конструкция типа yyin=fopen(fileName,"r"); не приведет к чтению файла анализатором. Вместо этого анализатор будет принимать символы со стандартного ввода. И опять нас выручает вдумчивое "курение" манов: после присвоения значения переменной yyin, необходимо вызвать функцию yyrestart(yyin); То есть указать новый поток ввода.

Подключаем bison

Bison - один из генераторов синтаксических анализаторов, наряду с yacc. Хоть в документации по Qt не ни слова о bison, но использовать я стал именно его. Причина проста - имеющийся в моем дистрибутиве yacc версии 1.9.1-r3 содержит в себе глюк (как я понял, исправленный в следующей версии), который исправить у меня никак не вышло. К тому же выяснилось, что попытка запустить yacc, когда он не установлен, приводит к запуску bison.

Итак, приступим. У нас есть готовый файл для yacc, нужно его подключить к проекту. Для этого надо в *.pro файл добавить строку вида YACCSOURCES = ../../flex_yacc/detect_cfg.y. Теперь Qt генерирует файл *yacc.cpp, содержащий синтаксический анализатор. Чтобы запустить синтаксический анализ некоторого файла из программы, нам нужно вставить вызов функции yyparse(). Компилятор честно скажет на это main.cpp:32: ошибка: нет декларации ‘yyparse’ в этой области видимости. Как и в случае с flex эту функцию надо объявить внешней (extern), и имя ее вовсе не yyparse() а зависит от имени *.y файла. В моем случае это была функция detect_cfgparse(). Объявление функции должно быть таким: extern int detect_cfgparse(void);. На detect_cfgparse() компилятор больше не ругается, зато появилось сообщение detect_cfg.tab.c:220: ошибка: нет декларации ‘detect_cfgerror’ в этой области видимости, которое значит что синтаксическому анализатору не хватает функции вывода ошибок. Нужно ее ему предоставить. Для этого создаем функцию void detect_cfgerror (char* s). Чтоб анализатор знал о ней необходимо добавить ее объявление в *.y файл как extern.

Теперь ваша программа должна заработать. Успехов! :-)


Контакты: e-mail; Гостевая книга

Сайт создан в системе uCoz