Комбинация утилит lex и yacc весьма эффективна для написания языковых интерпретаторов всех видов. Хотя большинству Unix-программистов никогда не придется выполнять данный вид универсального построения компилятора, для которого задумывались эти инструменты, они чрезвычайно полезны для написания анализаторов синтаксиса конфигурационных файлов и узкоспециальных мини-языков.
Сгенерированные с помощью lex анализаторы лексем работают очень быстро при распознавании низкоуровневых образцов во входных потоках, однако известный утилите lex язык регулярных выражений плохо подходит для вычисления или распознавания рекурсивно вложенных структур. Для их анализа потребуется yacc. С другой стороны, несмотря на то, что теоретически возможно написать yacc-грамматику с собственным сбором лексем, такая грамматика была бы перегружена кодом, а анализатор был бы крайне медленным. Для анализа входных лексем следует использовать lex. Таким образом, данные инструменты являются симбиотическими.
Если существует возможность реализовать анализатор на языке более высокого уровня, чем С (что и рекомендуется; см. главу 14), то следует рассмотреть такие
эквивалентные средства, как PLY в Python (которое охватывает функции lex и yacc ) [126] PLY можно загрузить со страницы .
или Perl-модули PY и Parse::Yapp, либо Java-пакеты CUP [127] Пакет CUP доступен на странице .
, Jack [128] Пакет Jack доступен на странице .
или Yacc/M [129] Пакет Yacc/M доступен на странице .
.
Как и в случае с макропроцессорами, одной из проблем, связанных с генераторами кода и препроцессорами, является то, что ошибки компиляции в сгенерированном коде могут содержать номера строк сгенерированного кода (который редактировать нежелательно), а не номера строк во входных данных генератора (т.е. там, где необходимо внести изменения). В утилитах yacc и lex данная проблема решается такими же конструкциями #line
, что и в препроцессоре С. Они устанавливают текущий номер строки для отчета об ошибках. Любая программа, генерирующая код на С или С++, должна работать аналогичным образом.
В более широком смысле хорошо спроектированные генераторы процедурного кода никогда не должны требовать от пользователя исправлять вручную или даже просматривать сгенерированный код. Создание корректного кода является непосредственной задачей генератора.
15.3.1.1. Учебный пример: грамматика fetchmailrc
Канонический демонстрационный пример, который, видимо, приводится в каждом учебном пособии по lex и yacc , представляет собой игрушечную программу интерактивного калькулятора, которая анализирует и вычисляет введенные пользователем арифметические выражения. В данной книге нет этого избитого клише. Заинтересованные читатели могут обратиться к исходному коду реализации bc(1) и dc(1) проекта GNU или к принципиальному примеру "hoc" [130] http://cm.bell-labs.com/cm/cs/upe/
см. [39].
Вместо этого грамматика анализатора конфигурационных файлов fetchmail предоставляет хороший учебный пример среднего размера по использованию lex и yacc . Здесь имеется несколько интересных моментов.
lex -спецификация в файле rcfile_l.l
— весьма типичная реализация shell-подобного синтаксиса. Обратите внимание на то, как два дополняющих правила поддерживают строки либо с одинарными, либо с двойными кавычками; данная идея хороша в принципе. Правила для принятия (возможно, со знаком) целых литералов и отклонения комментариев также являются достаточно распространенными.
yacc- спецификация в файле rcfile_y.y
достаточно длинная, но понятная. Она не осуществляет каких-либо fetchmail -действий, а только устанавливает биты в списке внутренних управляющих блоков. После запуска fetchmail в обычном режиме программа только периодически проходит по данному списку, используя каждую запись для управления сеансом получения почты с удаленного узла.
15.3.2. Учебный пример: Glade
Программа Glade рассматривалась в главе 8 в качестве хорошего примера декларативного мини-языка. Также отмечалось, что в результате работы серверной части Glade генерируется код на одном из нескольких языков.
Glade представляет собой хороший современный пример генератора прикладного кода. Описанные ниже функции, которые отсутствуют в большинстве GUI-построителей (особенно в большинстве коммерческих GUI-построителей), делают Glade. Unix-программой "по духу".
Читать дальше