Автоматизация процесса генерации компиляторов мультисинтаксических языков программирования

УДК 681.3

И. В. Ковалев, А. С. Кузнецов, Е. А. Веретенников

АВТОМАТИЗАЦИЯ ПРОЦЕССА ГЕНЕРАЦИИ КОМПИЛЯТОРОВ МУЛЬТИСИНТАКСИЧЕСКИХ ЯЗЫКОВ ПРОГРАММИРОВАНИЯ

Разработанная программная система повышает эффективность поиска ошибок в отлаживаемых программах синтаксического анализа на основе существующих спецификаций Flex и BYacc.

В настоящее время для создания отказоустойчивых программных систем существует несколько подходов. Среди них одним из наиболее очевидных и перспективных является методология мультиверсионного программного обеспечения [1].

Предположим, что программа или программная система, разработанная в рамках данной методологии, подразумевает, что во время ее выполнения запускаются несколько выполняющих одинаковые функции модулей, реализованных в виде задач (процессов или потоков) с изолированными или неизолированными адресными пространствами. Среда их выполнения может быть заранее известна, и в силу различных причин программная реализация этих модулей осуществляется на языках ассемблера, верификацию программ на котором приводить достаточно легко по сравнению с другими языками. Однако модули, реализующие функции по управлению или выполняющие интерактивные задачи, должны кодироваться на языках высокого уровня. Системы программирования для некоторых из них предлагают расширение таких языков с помощью так называемых ассемблерных вставок. Более общим случаем является использование ассемблерных фрагментов для нескольких микропроцессорных систем. И, наконец, в самом общем случае возможно одновременное использование в одном и том же исходном тексте нескольких языков высокого уровня (например, один модуль выполняется на виртуальной Java-машине, а другой - на виртуальной Lisp-машине). В любом случае в качестве лидера предполагается использование единственного языка.

Таким образом, уже в рамках одного исходного текста (или части) программы могут одновременно использоваться языки с различающимися свойствами с точки зрения синтаксиса и, как следствие, генерируемые разными классами грамматик. В связи с этим можно отнести такие языки к классу мультисинтаксических (МСЯ). Очень важно, что для анализа таких языков в общем случае может потребоваться использование в качестве основы грамматики 0-типа в соответствии с иерархией Хомского [2].

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

жет и отличаться от числа составляющих языков, т. е. они могут быть неоднозначными. Это, а также возможные действия по синтезу целевого кода (во всех смыслах) программы и должны образовать компилятор требуемого МСЯ, который уже в дальнейшем может использоваться для создания мультиверсионного программного обеспечения.

Формально синтаксис МСЯ Ь можно описать следующим образом с использованием списка грамматик О:

о = (о1еаа, ои..., оI, оN),

где О1еаЛ - грамматика языка-лидера, остальные грамматики О1 являются вспомогательными, которые обладают всеми свойствами контекстно-свободных грамматик [2]; N - количество грамматик «вспомогательных языков».

В то же самое время продукции грамматики Оы внешне выглядят как контекстно-свободные, но со следующими отличиями. О1еаЛ определяется шестеркой элементов:

ОШ = ( = N = Р = ^ У^НсИ = Р*ш!сИ X где Т, N Р, S имеет тот же смысл, что и в [2], УШ1ск - множество особых лексем, которое является подмножеством множества терминальных символов Т; РшисН - специально выделенное множество продукций для переключения между грамматиками, которое является подмножеством всего множества продукций. Фактически, это пары вида а ^ ЛД где а - левая часть продукции; Л1 - специальный терминал (Д &У!М,сИ), который вызывает смену грамматики с О1еаЛ на О; в - хвост правой части продукции, который содержит специально выделенный нетерминал, именующий группу продукций грамматики О..

Естественно, всю совокупность синтаксических анализаторов можно рассматривать как единый модуль, но для удобства автоматизации их создания удобнее считать их отдельными модулями (рис. 1).

Рис. 1. Схема взаимодействия синтаксических анализаторов

С целью автоматизации процесса генерации компиляторов МСЯ предлагается разработать так называемый «компилятор компиляторов». Для этого разумнее воспользоваться каким-либо хорошо зарекомендовавшим себя средством, модифицировать в соответствии с поставленной задачей, осуществить его отладку и внедрить в эксплуатацию. Как

упоминалось в статье [3], наиболее подходящими инструментами для этого являются родственные друг другу программы Bison и Byacc. Они строят программы восходящего синтаксического анализа, работающие по алгоритму LALR(1), покрывая большинство практически применяемых языков программирования. В той же работе [3] описывается разработка программы под рабочим названием MuYacc, где для автоматизации лексического анализа используется механизм регулярных выражений, а для автоматизации синтаксического анализа - система продукций. Остается учесть также следующие ограничения и рекомендации, тесно взаимосвязанные между собой:

- «вспомогательные» синтаксические анализаторы горизонтально другом с другом не взаимодействуют, чтобы избежать петель вызовов процедур анализа (рис. 2). Это означает, что перед обработкой входных спецификаций осуществляется проверка наличия таких петель;

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

- действия по проверке семантических соглашений и действия этапа синтеза выполняются на языке программирования либо описываются в формате XML;

- в качестве лидера используется какой-либо из языков промышленного программирования, например Си++.

Рис. 2. Петли вызовов анализаторов и несовпадение языка-лидера и вспомогательного языка

Следует отметить, что реализация предлагаемого подхода и разработка данного компилятора компиляторов необходимы для использования в учебном процессе студентов программистских специальностей и смежных с ними по дисциплинам «Теория языков программирования и методы трансляции», «Основы построения трансляторов», «Системное программное обеспечение», «Разработка программного обеспечения для информацион-но-управляющих систем». Также возможно его применение в промышленной эксплуатации при реализации различных этапов и фаз процесса трансляции языков программирования и при создании программного обеспечения информационно-управляющих систем.

Входная спецификация инструментального средства разработки компиляторов МСЯ. В основе проекта с рабочим названием MuYacc лежит входная спецификация BYacc, которая в общем случае состоит из трех частей, разделенных символами %%:

- объявления,

%%

- правила,

%%

- пользовательские функции.

В разделе объявлений перечисляются терминальные символы алфавита реализуемого языка программирования, предваренные ключевым словом % token, а также указываются некоторые правила разрешения неоднозначности в грамматике реализуемого языка. Правила этой грамматики перечисляются во второй части спецификации, а лексический анализатор пишется на языке регулярных выражений в первой части входной спецификации. Другие процедуры определяются в третьей части.

При создании автоматизированного средства разработки трансляторов языков программирования возможны два пути реализации лексических анализаторов с использованием регулярных выражений, которые можно разместить в разделе объявлений и в разделе пользовательских функций.

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

Для этого во входной спецификации используется ключевое слово % regexp, которое определяет конкретное регулярное выражение (его имя, описание, действия), даже если оно не используется в качестве терминального символа в алфавите реализуемого языка и не встречается в продукциях его грамматики. Чтобы описать терминальный символ, достаточно создать регулярное выражение с таким же именем. Далее приведен фрагмент с описанием терминальных символов и продукции упрощенной грамматики для арифметических выражений:

%regexp letter [a-z]

%regexp digit [0-9]

%regexp IDENT letter(letter|digit)*

%action% {strcpy($$,yytext);} %regexp NUM digit+ %action% {$$=atoi(yytext);}

%token <str> IDENT %token <num> NUM

expr : expr '+' expr | '(' expr ')' |

IDENT | NUM;

Введение конструкции % regexp позволяет использовать уже имеющиеся спецификации YACC/Byacc практически без изменений, для этого достаточно добавить в них строки описания лексем; использовать уже имеющиеся программные средства. На основе приведенного формата спецификации довольно легко сгенерировать спецификации для Flex и BYacc, а затем сгенерировать с их помощью исходные тексты частей транслятора.

При генерации спецификации для Flex и BYacc некоторую трудность представляет формирование спецификации Flex. Для того чтобы ее получить, необходимо обработать все конструкции % regexp и % token. В спецификацию BYacc достаточно просто скопировать все, что не касается % regexp.

Возможность описания мультисинтаксиса реализуется четырьмя новыми ключевыми словами: % tokengroup, % endtokengroup, % syntaxgroup, % endsyntaxgroup,

размещаемыми в первой и второй частях входной спецификации и обладающих описанными ниже свойствами соответственно.

Ключевые слова % tokengroup и % endtokengroup играют роль операторных скобок, в которые заключаются группы регулярных выражений. Аналогично для групп продукций в качестве скобок выступают слова % synta-xgroup и % endsyntaxgroup. Обе «открывающие» скобки именуют начало группы и дают название процедурам, соответственно, лексического и синтаксического анализа для их дальнейшего использования (свойство name). Для групп продукций также указывается название «особенной лексемы» (свойство kword), а также наименование соответствующей групп лексем и регулярных выражений (свойство tokengroup). Кроме того, предусмотрена возможность импорта групп лексем и продукций из внешних файлов, при этом осуществляется проверка наличия таковых. Вложенность групп запрещается по описанным выше причинам.

Для обозначения «особенной лексемы» во входной спецификации анализатора-лидера можно использовать еще одно ключевое слово % switchtoken. Данная лексема обязана дважды присутствовать в спецификации: как обычная лексема и как «особенная». Приведем примеры возможных спецификаций (действия по генерации кода не приводятся).

1. Без импорта из внешнего файла:

%tokengroup name="arith tokens"

%regexp letter [a-z]

%regexp digit [0-9]

%regexp IDENT letter(letter|digit)*

%action% {strcpy($$,yytext);} %regexp NUM digit+ %action%

{$$=atoi(yytext);}

%token <str> IDENT %token <num> NUM %endtokengroup %switchtoken ARITH

analyzer :

| analyzer '\n'

| expressions analyzer

expressions : ARITH '{' arith expressions

'}'

%syntaxgroup name="arith expressions" kword=ARITH

tokengroup="arith tokens"

expr : expr '+' expr | '(' expr ')' | IDENT

%endsyntaxgroup

2. С импортом из внешнего файла:

%tokengroup name="arith tokens"

%endtokengroup

%syntaxgroup name="arith expressions" kword="arith" tokengroup="arith tokens"

%endsyntaxgroup

Получаемый в результате работы программы усовершенствованный синтаксический анализатор языка-лиде-ра при распознавании «особенной лексемы» временно приостанавливается, и осуществляется переключение на действия соответствующего вспомогательного анализатора, по завершению работы которого выполняется возврат наверх по иерархии анализаторов. В остальном лидер работает аналогично обычному синтаксическому анализатору.

Таким образом, описанный подход способствует заметному уменьшению времени на разработку компилятора, облегчает поиск ошибок в отлаживаемых программах синтаксического анализа на основе существующих спецификаций Flex и BYacc, а также снижает вероятность их появления за счет использования автоматизированного средства. Дальнейшая реализация автоматизированного средства должна подтвердить пользу от внедрения в практику разрабатываемой программы компиляции мульти-синтаксических языков программирования, в особенности для мультиверсионного программного обеспечения.

Библиографический список

1. Ковалев, И. В. Программная система «NVX v1.0» (среда мультиверсионного исполнения программных модулей) / И. В. Ковалев [и др.] // Компьютерные учебные программы и инновации. 2005, № 6.

2. Хантер, Р. Основные концепции компиляторов / Р. Хантер. М. : Издательский дом «Вильямс», 2002. 256с.

3. Кузнецов, А. С. Инструментальное средство разработки трансляторов мультисинтаксических языков программирования / А. С. Кузнецов // Вестник университетского комплекса : сб. науч. тр. ; Красноярск : ВСФ РГУ-ИТП, НИИ СУВПТ. 2006. Вып. 7(21). С. 227-230.

I. V. Kovalev, A. S. Kuznecov, E. A. Veretennikov

AUTOMATION OF PROCESS OF MULTI-SYNTAX PROGRAMMING LANGUAGE COMPILERS GENERATION

The developed program system raises the errors search effectiveness into the syntactical analysis programs based on the Flex u BYacc specifications.