Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лекции / вар1 / OPYZK.DOC
Скачиваний:
14
Добавлен:
17.04.2013
Размер:
173.06 Кб
Скачать

3. Пример программы разбора

Применение метаязыка и генератора языковых процессоров проиллюст-

рируем на примере разработки программы, моделирующей работу калькуля-

тора с памятью. Эта программа представляет собой простой языковый про-

цессор, осуществляющий синтаксический анализ и расчет обычных арифме-

тических выражений.

Программу будем описывать на метаязыке по группам продукций с по-

яснением каждой группы.

<FULL_EXPR> /* начальный символ грамматики*/

::= *

#{ i = 1;}#

<EXPR>

#{ MEMORY = Expression[i];

cout << Expression[i];

}#

||= <FULL_EXPR> ','

#{i = 1;}#

<EXPR>

#{ MEMORY = Expression[i];

cout << Expression[i];

}#

Исходное предложение представляет собой выражения, перечисленные

через запятую. Для промежуточных вычислений используется массив Exp-

ression с индексом i. Начальное значение индекса - 1. Для ввода на-

чального значения в самом начале вычислений нам ничто не мешает ввести

в грамматику пустой символ *. Результат вычислений в конечном итоге

находится в первом элементе массива Expression[1]. По окончании вычис-

ления одного выражения результат заносится в память MEMORY.

<EXPR> /* Расчет выражения */

- 26 -

::= <SU>

#{ Expression[i] = Summand[i];}#

||= <EXPR> ( '+' <SU>

#{ Expression[i] += Summand[i];}#

| '-' <SU>

#{ Expression[i] -= Summand[i];}#

)

Выражение можно представить в виде одного слагаемого или набора

слагаемых, соединенных знаком плюс или минус. По окончании вычислений

выражения результат находится в элементе стека Expression[i], а ре-

зультат вычислений при разборе слагаемого в Summand[i].

<SU> /* Расчет значения слагаемого */

::= <MU>

#{ Summand[i] = Multiplier;}#

||= <SU> ( '*' <MU>

#{ Summand[i] *= Multiplier;}#

| '/' <MU>

#{ Summand[i] /= Multiplier;}#

)

Как видно из грамматики, слагаемое представляется одним множите-

лем или несколькими, соединенными знаками умножения и деления. Резуль-

тат вычисления слагаемого, как было отмечено и раньше, заносится в

Summand[i]. Значение множителя находится в переменной Multiplier.

.

- 27 -

<MU> /* Разбор и вычисление множителя */

::= <DEC>

#{ Multiplier = Decimal; }#

||= 'M'

#{ Multiplier = MEMORY;}#

||= '('

#{ i++;}#

<EXPR>

')'

#{ Multiplier = Expression[i];

i--;

}#

В качестве множителя может выступать десятичное число, значение

предыдущего выражения, находящееся в памяти MEMORY, и выражение в

скобках. Результатом разбора множителя является значение, записываемое

в переменную Multiplier.

Десятичное число вычисляется без учета десятичной точки при раз-

боре группы продукций под именем DEC и записывается в переменную Deci-

mal. В данном фрагменте число приводится в соответствие с наличием

дробной части.

При вычислении выражения в скобках увеличивается индекс массива

после появления открывающей скобки, поскольку начинается вычисление

нового выражения (которое в скобках), но в дальнейшем может понадо-

биться и результат предыдущих вычислений. Значение выражения в скобках

по окончании его расчета является множителем и поэтому заносится в пе-

ременную Multiplier. Далее уменьшается индекс массива и открывается

доступ к вычисленному ранее текущему значению выражения.

<DEC> /*программа разбора десятичного числа*/

::= {09}

#{ strcpy(str,token);}#

- 28 -

( .{09}

#{ strcat(str,token);}#

| *

)

#{ Decimal = atof(str);}#

Описания переменных помещаются в отдельном модуле. Приведенная

программа с помощью генератора языковых процессоров переводится на

язык СИ и сохраняется в виде отдельного модуля. Сгенерированные модули

и модуль описаний обрабатываются транслятором. На этапе компоновки

подключается стандартный объектный модуль и на выходе получается гото-

вая программа расчета выражений. Вся последовательность действий по

генерации производится автоматически до получения загрузочного модуля

транслятора. Генератор запрашивает лишь имя начального символа грамма-

тики и имя модуля описаний переменных. Примером выражения может слу-

жить следующая строка

3.23*5.62*(23.5+56.59-156.456)-1568.2+2.2458*(26.54+0.0015)+

12.5*0.25-1

.

- 29 -

4. ОДНОЗНАЧНОСТЬ ГРАММАТИКИ

 14.1. Вывод исходного предложения

Определенная метаязыком грамматика рекурсивным образом определяет

порождаемый язык, который содержит множество выводимых из начального

нетерминального символа строк. Определим формально выводимую строку.

Строка <x><w><y> называется выводимой строкой грамматики , если

<v> ::= <w> ( или <v> ||= <W>) продукция описания на метаязыке и

<x><v><y> строка терминальных и нетерминальных символов длиной > 1,

принадлежащая языку, порождаемому грамматикой. Выводимая строка полу-

чается путем замены строки <v> на строку <w> в строке <x><v><y>. Такое

преобразование строки записывают в виде

<x><v><y> => <x><w><y>

Отношение =*> задается как <x1> =*> <xN>, если существует после-

довательность <x1>,<x2>,..., <xN>, такая что <x1> => <x2> => <x3>

=>...=> <xN>. В этом случае говорят, что строка <xN> выводится из

строки <x1> за один и более шагов.

Если строка <x> является выводимой из начального символа строкой

(<S> => <x>), то ее называют сентенциальной формой. Не содержащая не-

терминальных символов сентенциальная форма называется предложением

(или сентенцией). Множество всех предложений грамматики называют язы-

ком, порождаемым этой грамматикой.

Рассмотрим примеры предложений нескольких языков. Для приведенной

выше грамматики (раздел 1.), с начальным символом <целое> в язык вклю-

чены следующие предложения, представляющие собой десятичные числа.

2 вывод <целое> => <цифра> => 2

23 вывод <целое> => <целое><цифра> ( правило

<целое> ||= <целое><цифра>)

=> <цифра><цифра> (правило <целое> ::= <цифра>)

- 30 -

=> 2<цифра> (правило <цифра> ||= 2 )

=> 23 (правило <цифра> ||= 3 )

Если грамматику задать в виде

<целое> ::= string{09}

то строки - целые числа выводятся за один шаг.

3454 вывод <целое> => 3454

С помощью метаязыка мы определили язык. Однако нам необходимо от-

ветить на вопрос принадлежит исходное предложение этому языку или нет.

Используемый генератором метод разбора будет правильно работать, если

для каждого исходного предложения существует только один вывод. Не на-

рушая общности упорядочим процесс вывода предложения.

При разборе будем использовать только левосторонний вывод. Это

такой вывод, когда на каждом шаге вывода заменяется самый левый из не-

терминальных символов. Причем варианты для замены будем выбирать в по-

рядке их описания на метаязыке слева-направо сверху-вниз. Придержива-

ясь указанного порядка выполняется первая подходящая замена левого не-

терминального символа, которая ведет к выводу данного исходного пред-

ложения.

Соседние файлы в папке вар1