Многие люди, обычно использующие оба редактора vi и Emacs, склонны применять их для различных задач и находят весьма ценными преимущества использования обоих.

Вообще, vi наилучшим образом подходит для решения мелких задач — быстрого написания ответов на письма, простых изменений в конфигурации системы и т.д. Данный редактор особенно полезен при работе в новой системе (или на удаленной системе через сеть), когда не доступны файлы настроек Emacs.

Роль Emacs — расширенные сеансы редактирования, в которых необходимо решать сложные задачи, модифицировать несколько файлов и использовать результаты работы других программ в течение данного сеанса. Для программистов, использующих систему X на своих консолях (что типично для современных Unix-систем), считается обычным запускать Emacs сразу после регистрации в системе в большом окне и оставлять его работающим постоянно, возможно, просматривая десятки файлов и даже запуская программы в многочисленных подокнах Emacs.

15.3. Генераторы специализированного кода

Unix имеет давнюю традицию поддержки инструментов, которые специально предназначены для генерации кода для различных специальных целей. Давними 'монументами' данной традиции, которые 'уходят корнями' в Version 7 и ранние дни Unix, а также фактически использовались для написания оригинального Portable С Compiler в 1970-х годах, являются утилиты lex(1) и yacc(1). Их современными совместимыми потомками являются flex (1) и bison(1), часть GNU-инструментария, которая до сих пор интенсивно используется и в наши дни. Данные программы послужили примером, который продолжает развиваться в проектах, подобных построителю интерфейсов Glade в GNOME.

15.3.1. yacc и lex

Программы yacc и lex являются инструментальными средствами для генерации синтаксических анализаторов языков программирования. В главе 8 отмечалось, что свой первый мини-язык программист часто создает случайно, а не как часть запланированной конструкции. В результате, как правило, появляется созданный вручную синтаксический анализатор, который приводит к чрезмерным затратам времени на сопровождение и отладку, особенно, если разработчик не поймет, что часть разработанного им кода является синтаксическим анализатором и не отделит его соответствующим образом от остальной части кода приложения. Генераторы синтаксических анализаторов являются инструментами, которые позволяют добиться большего, чем создание случайной, узкоспециальной реализации. Они не только позволяют разработчику выразить спецификацию грамматики на более высоком уровне, но и четко отделяют сложность реализации синтаксического анализатора от остального кода.

Если разработчик достиг того момента, когда планируется реализовать мини- язык с нуля, а не путем расширения или внедрения существующего языка сценариев или анализатора XML, то утилиты yacc и lex, вероятно, окажутся наиболее важными инструментами после компилятора С.

Как lex, так и yacc генерируют код для одной функции, соответственно, для 'получения лексемы из входного потока' и для 'синтаксического анализа последовательности лексем на предмет ее соответствия грамматике'. Обычно созданная yacc функция синтаксического анализатора вызывает функцию анализатора лексем, сгенерированного lex, каждый раз при необходимости получения следующей лексемы. Если в yacc-сгенерированном синтаксическом анализаторе вообще не существует написанных пользователем обратных вызовов С, то работа данного анализатора сводится только к проверке синтаксиса. Возвращаемое значение сообщит вызывающей программе о совпадении входных данных с ожидаемой грамматикой.

В более распространенном варианте пользовательский C-код, встроенный в сгенерированный синтаксический анализатор, заполняет некоторые динамические структуры данных как побочный эффект синтаксического анализа ввода. Если мини-язык является декларативным, то приложение может использовать эти структуры данных непосредственно. В случае императивного мини-языка структуры данных могут включать в себя дерево грамматического разбора, которое немедленно передается некоторой оценочной функции.

Утилита yacc имеет довольно некрасивый интерфейс — через экспортируемые глобальные переменные с именным префиксом yy_. Это связано с тем, что программа yacc предшествовала структурам С. Фактически yacc предшествовала самому языку С; первая реализация утилиты была написана на языке В, предшественнике С. Грубый, хотя и эффективный алгоритм, используемый в сгенерированных yacc синтаксических анализаторах при попытках восстановления после ошибок анализа (лексемы выталкиваются до тех пор, пока не обнаружится явная ошибка), также может привести к проблемам, включая утечки памяти.

Если вы создаете деревья грамматического разбора с использованием функции malloc и начинаете выталкивать элементы стека в процессе восстановления после ошибки, то вам не удастся восстановить (высвободить) память. Как правило, утилита yacc не способна это делать, поскольку не имеет достаточных сведений о содержимом стека. Если бы yacc-анализатор был написан на С++, то он мог бы 'предположить', что значения являются классами, и использовать деструктор. В 'реальных' компиляторах узлы дерева грамматического разбора генерируются с помощью распределителя динамической памяти, поэтому узлы 'не вытекают', но так или иначе, проявляется логическая утечка памяти, которую необходимо проанализировать, чтобы создать систему восстановления после ошибок промышленного уровня.

Стив Джонсон.

Программа lex — генератор лексических анализаторов. Она входит в состав того же функционального семейства, что и grep(1) и awk(1) , но является более мощной, поскольку позволяет подготовить произвольный С-код для выполнения при каждом совпадении. Программа принимает декларативный мини-язык и создает 'скелетный' С-код.

Для того чтобы понять работу lex-сгенерированного анализатора лексем, существует грубый, но удобный способ — мысленная инверсия работы grep(1). Тогда как grep (1) принимает одно регулярное выражение и возвращает список совпадений во входном потоке данных, каждый вызов lex-сгенерированного анализатора лексем принимает список регулярных выражений и указывает, какое выражение встречается следующим в потоке данных.

Разделение анализа ввода на распознавание лексем и синтаксический анализ потока лексем является полезным тактическим приемом, даже если в ходе разработки утилиты Yacc и Lex не используются, а 'лексемы' не имеют ничего общего с обычными лексемами в компиляторе. Неоднократно я убеждался, что разделение обработки входных данных на два уровня значительно упрощает код и облегчает

Добавить отзыв
ВСЕ ОТЗЫВЫ О КНИГЕ В ИЗБРАННОЕ

0

Вы можете отметить интересные вам фрагменты текста, которые будут доступны по уникальной ссылке в адресной строке браузера.

Отметить Добавить цитату