Майк Леек.
Важнее то, что мини-язкк /едг-спецификации является более высокоуровневым и компактным, чем эквивалентный С-код, написанный вручную. Доступны модули для использования flex (версия с открытым исходным кодом) с Perl (их можно найти в Web с помощью фразы "lex perl"), а также идентично работающая реализация, которая является частью средства PLY в Python.
lex генерирует синтаксические анализаторы, работающие на порядок медленнее написанных вручную. Однако данный факт не является причиной для ручного кодирования, это аргумент в пользу создания с помощью lex прототипа и доработки кода вручную, только если прототип показывает реальное "бутылочное горлышко".
Утилита уасс — генератор синтаксических анализаторов. Она также была написана для автоматизации части работы по написанию компиляторов, уасс принимает на входе грамматическую спецификацию в декларативном мини-языке, подобном BNF (Backus-Naur Form — запись Бэкуса-Наура), с С-кодом, связанным с каждым элементом грамматики. Данная программа генерирует код для функции синтаксического анализа, которая при вызове принимает текст, соответствующий грамматике из входного потока. По мере распознавания каждого грамматического элемента, функция анализатора запускает связанный С-код.
Комбинация утилит lex и уасс весьма эффективна для написания языковых интерпретаторов всех видов. Хотя большинству Unix-программистов никогда не придется выполнять данный вид универсального построения компилятора, для которого задумывались эти инструменты, они чрезвычайно полезны для написания анализаторов синтаксиса конфигурационных файлов и узкоспециальных мини-языков.
Сгенерированные с помощью lex анализаторы лексем работают очень быстро при распознавании низкоуровневых образцов во входных потоках, однако известный утилите lex язык регулярных выражений плохо подходит для вычисления или распознавания рекурсивно вложенных структур. Для их анализа потребуется уасс. С другой стороны, несмотря на то, что теоретически возможно написать yacc-грамматику с собственным сбором лексем, такая грамматика была бы перегружена кодом, а анализатор был бы крайне медленным. Для анализа входных лексем следует использовать lex. Таким образом, данные инструменты являются симбиотическими.
Если существует возможность реализовать анализатор на языке более высокого уровня, чем С (что и рекомендуется; см. главу 14), то следует рассмотреть такие эквивалентные средства, как PLY в Python (которое охватывает функции lex и уасс)95или Perl-модули PY и Parse::Yapp, либо Java-пакеты CUP96, Jack97 или Yacc/M98.
Как и в случае с макропроцессорами, одной из проблем, связанных с генераторами кода и препроцессорами, является то, что ошибки компиляции в сгенерированном коде могут содержать номера строк сгенерированного кода (который редактировать нежелательно), а не номера строк во входных данных генератора (т.е. там, где необходимо внести изменения). В утилитах уасс и lex данная проблема решается такими же конструкциями #line, что и в препроцессоре С. Они устанавливают текущий номер строки для отчета об ошибках. Любая программа, генерирующая код на С или С++, должна работать аналогичным образом.
В более широком смысле хорошо спроектированные генераторы процедурного кода никогда не должны требовать от пользователя исправлять вручную или даже просматривать сгенерированный код. Создание корректного кода является непосредственной задачей генератора.
15.3.1.1. Учебный пример: грамматика fetchmailrc
Канонический демонстрационный пример, который, видимо, приводится в каждом учебном пособии по lex и уасс, представляет собой игрушечную программу интерактивного калькулятора, которая анализирует и вычисляет введенные пользователем арифметические выражения. В данной книге нет этого избитого клише. Заинтересованные читатели могут обратиться к исходному коду реализации bс( 1) и dc( 1) проекта GNU или к принципиальному примеру "hoc"99 см. [39].
Вместо этого грамматика анализатора конфигурационных файлов fetchmail предоставляет хороший учебный пример среднего размера по использованию lex и уасс. Здесь имеется несколько интересных моментов.
/ег-спецификация в файле rcfile_l.l— весьма типичная реализация shell-подобного синтаксиса. Обратите внимание на то, как два дополняющих правила поддерживают строки либо с одинарными, либо с двойными кавычками; данная идея хороша в принципе. Правила для принятия (возможно, со знаком) целых литералов и отклонения комментариев также являются достаточно распространенными.
уасс-спецификация в файле rcfile_y.y достаточно длинная, но понятная. Она не осуществляет каких-либо fetchmail-действий, а только устанавливает биты в списке внутренних управляющих блоков. После запуска fetchmail в обычном режиме программа только периодически проходит по данному списку, используя каждую запись для управления сеансом получения почты с удаленного узла.