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

14.2. Преобразование грамматики

Для обеспечения однозначного вывода необходимо для каждого нетер-

минального символа преобразовать все его продукции таким образом, что-

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

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

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

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

ных выше примеров.

<E> ::= <E> + <T>

||= <E> - <T>

||= <T>

- 31 -

<T> ::= string{09}

Первые две альтернативы для нетерминала <E> начинаются с одного и

того же нетерминального символа <E>. Поэтому при выводе непонятно, ка-

кую из альтернатив необходимо выбрать, если руководствоваться только

начальными символами. То есть при выводе предложения 45 - 56 по первым

цифрам 45 невозможно определить какую альтернативу выбрать. Поэтому

грамматику нужно преобразовать. Введем новый нетерминал <D> для обоз-

начения различающихся частей первых двух альтернатив.

<D> ::= + <T>

||= - <T>

Тогда правила для нетерминала <E> примут следующий вид

<E> ::= <E><D>

||= <T>

Отметим, что используемый метод разбора допускает прямую левую

рекурсию. При разборе эта эти два правила автоматически будут рассмат-

риваться как <E> ::= <T> <* <D> *>_. Поэтому их дальнейшее преобразо-

вание можно опустить.

Вывод строки 44 - 56 будет следующим

<E> => <T><*<D>*>_ => string{09}<*<D>*>_ => 45<*<D>*>_

=> 45<D><*<D>*>_ => 45-<T><*<D>*>_ => 45-string{09}<*<D>*>

=> 45-56<*<D>*>_ => 45-56

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

скобки.

<E> ::= <E> ( + <T> | - <T>)

||= <T>

что эквивалентно <E> ::= <T> <* (+<T> | -<T>) *>_.

Вывод той же строки будет в этом случае таким

<E> => <T><*(+<T>|-<T>)*>_ => string{09}<*(+<T>|-<T>)*>_

=> 45<*(+<T>|-<T>)*>_ => 45(+<T>|-<T>)<*(+<T>|-<T>)*>_

=> 45-<T><*(+<T>|-<T>)*>_ => 45-string{09}<*(+<T>|-<T>)*>_

- 32 -

=> 45-56<*(+<T>|-<T>)*>_ => 45-56

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

писанных в них. В заключении данного параграфа отметим, что правила

начинающиеся с одинакового символа левой рекурсии, не обязательно

группировать.

<E> ::= <E> + <T>

||= <E> - <T>

||= <T>

автоматически заменятся на

<E> ::= <T> <* (+<T> | -<T>) *>_

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

рекурсию, необходимо выполнять описанные выше преобразования.

Левый рекурсивный цикл необходимо всегда сводить к прямой левой

рекурсии. Например, если

<E> ::= <B>x

<B> ::= <E>y

то необходимо преобразовать в

<E> ::= <E>yx

14.3. Примеры преобразования грамматик на метаязыке

Пример 4.3.1. Рассмотрим грамматику

<A> ::= a<A>y

||= a'b'<A>x

Вынесем за скобки терминальный символ a. Получим

<A> ::= a (<A>y | 'b'<A>x)

Две альтернативы в скобка начинаются с различных терминальных

символов a и b.

Пример 4.3.2. Рассмотрим грамматику

<A> ::= a<A>y

- 33 -

||= a'b'<A>x

||= a

Поступаем аналогично предыдущему примеру.

<A> ::= a (<A>y | 'b'<A>x | *)

Напомним, что * обозначает пустой символ (отсутствие символа).

Пример 4.3.3. Рассмотрим грамматику

<A> ::= a<A>y

||= a'b'z<A>x

||= <A>b<A>y

||= a

Все альтернативы могут начинаются с символа a. Подставим в третью

альтернативу вместо <A> четвертую продукцию и введем её в качестве до-

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

тую продукцию, поскольку за символом <A> в третьей продукции следует

b. Такая подстановка выявляет неоднозначность между второй и третьей

исходной продукциями (они начинаются с символов 'a''b').

<A> ::= a<A>y // 1 - альтернатива

||= a'b'z<A>x // 2

||= 'a'b<A>y // 3

||= <A>b<A>y // 4

||= a // 5

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

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

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

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

<A> ::= a (<A>y | 'b'z<A>x | b<A>y | * ) | <A>b<A>y

В скобках получили альтернативы, начинающиеся с одного и того же

символа. Продолжим преобразования и получим.

<A> ::= a (<A>y | 'b'(z<A>x | <A>y) | * ) | <A>b<A>y

Необходимо строго соблюдать, чтобы четвертая альтернатива была

- 34 -

после третьей. Окончательно получим грамматику.

Рассмотрим еще один вариант преобразования данной грамматики.

<A> ::= a<A>y

||= a'b'z<A>x

||= <A>b<A>y

||= a

Леворекурсивный цикл автоматически приводится к виду

<A> ::= (a<A>y | a'b'z<A>x | а) <* b<A>y *>_

или

<A> ::= a(<A>y | 'b'z<A>x | *) <* b<A>y *>_

Видно, что при конкатенации пустого символа * и со строкой за

круглыми скобками, можно получить строку, начинающуюся с символа b. В

скобках у нас уже есть альтернатива начинающаяся также с символа b.

Поэтому выполним следующие преобразования.

<A> ::= a(<A>y | 'b'z<A>x | * | b<A>y) <* b<A>y *>_

<A> ::= a(<A>y | b (z<A>x | <A>y) | *) <* b<A>y *>_

Обратное преобразование к левой рекурсии.

<A> ::= a(<A>y | b (z<A>x | <A>y) | *)

||= <A>b<A>y

<A> ::= a(<A>y | b (z<A>x | <A>y) | * ) | <A>b<A>y

Пример 4.3.4. Рассмотрим грамматику

<A> ::= x<A>y

||= <P>

||= <Q>

||= x'x'<Z>

<P> ::= x<P>m

||= p

<Q> ::= x<Q>n

||= q

Стандартное преобразование приводит к тому, что в скобках альтер-

- 35 -

нативы начинаются снова с символов <P> и <Q> и так до бесконечности.

<A> ::= x<A>y --¬

||= x<P>m ¦

||= x<Q>n ¦ => <A> ::= x(A>y | <P>m | <Q>n | 'x'<Z>)

||= x'x'<Z> ¦ ||= p

||= p ¦ ||= q

||= q ---

В этом случае к цели приводит следующий путь.

<A> ::= x<A>y --¬ <A> ::= x'x'^_<A>'y'^_y

||= <P> ¦ => ||= x^_<P>m^_

||= <Q> ¦ ||= x^_<Q>n^_

||= x'x'<Z> --- ||= x'x'<Z>

<A> ::= x'x'^_<A>'y'^_y

||= x'x'^_<P>m^_m

||= x'x'^_<Q>n^_n

||= x'x'<Z>

||= <P>

||= <Q>

<A> ::= x('x'^_<A>'y'^_y | 'x'^_<P>m^_m | 'x'^_<Q>n^_n | 'x'<Z>)

<A> ::= x('x'^_<A>'y'^_y|'x'^_<P>m^_m|'x'^_<Q>n^_n|'x'^1<Z>*^1)

<A> ::= x('x'^_(<A>'y'^_y | <P>m^_m | <Q>n^_n | <Z>*^1))

Поскольку x^ может представлять и x^1 в частном случае, её можно

вынести за скобки. Заменим в первой альтернативе <A>, поскольку она

содержит одинаковые символы с <P> и <Q>.

<A> ::= x('x'^_('x'^_(<P>m^_'y'^_y |<Q>n^_'y'^_y |<A>'y'^_y |

<P>m^_m^_m | <Q>n^_n^_n) | <Z>*^1))

Хотя в скобках снова остались <Q> и <P> они уже не могут начаться

с символа x, поскольку все символы x вынесены за скобку.

.

- 36 -

<A> ::= x( 'x'^_( 'x'^_( <P>m^_('y'^_y |m^_m) |

<Q>n^_('y'^_y |n^_n) |

<A>'y'^_y

) | <Z>*^1

)

)

Окончательно получим

<A> ::= x( 'x'^_( ( <P>m^_('y'^_y |m^_m) |

<Q>n^_('y'^_y |n^_n) |

<A>'y'^_y

) | <Z>*^1

)

)

||= <P>

||= <Q>

Необходимо заметить, что при использование степеней нужно ис-

пользовать только вынос за скобки. Вводить новые нетерминалы запрещено

( это ограничение не метаязыка, а реализации )

Перечислим правила, которые использовались при преобразовании

грамматик.

- Подстановка вместо нетерминала его продукций.

- Вынос общей части продукций за круглые скобки.

- Добавление новой продукции, которая может быть порождена одной

из исходных продукций, для выявления продукций с одинаковыми начальны-

ми частями. Новая продукция помещается перед порождающей её продукцией.

- Замена группы продукций новым нетерминальным символом.

.

- 37 -

ПРИЛОЖЕНИЕ 1. Синтаксис метаязыка, записанный на метаязыке

Все тексты на метаязыке могут содержать любое число пробельных

символов, к которым относится и комментарий. К этой группе символов

относятся, собственно символы пробелов, символы вертикальной и гори-

зонтальной табуляции, символы новой строки (перевода строки и возврата

каретки) и комментарии.

<пробельные символы> ::= string{\11, /* HT */

\12, /* LF */

\13, /* VT */

\15, /* CR */

\40 /* whitespace */

}

||= <комментарий>

Комментарии используются для пояснения текста программы. Су-

ществует два способа задания комментариев:

<комментарий> ::= '/*' <текст комментария>_ '*/'

||= '//' <строчный комментарий>_

<текст комментария> ::= !string{'/*','*/'}

<строчный комментарий> ::= !string{\12,\15}

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

имеет пробельных символов. Все пробельные символы пропускаются в ис-

ходном тексте и используются в виде разделителей неявно. Явно в грам-

матике метаязыка они не представлены.

/*------------------------------------------------------------------*/

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

- 38 -

<метаязык> ::= <нетерминал> <* <правило> *>

/*------------------------------------------------------------------*/

<нетерминал> ::= '<' <нетерминальный_символ> '>'

/*------------------------------------------------------------------*/

<правило > ::= <нетерминал>_

'::=' <альтернатива><*<следующая_альтернатива>*>_

/*------------------------------------------------------------------*/

<нетерминальный_символ>

::= string {az,AZ,ая,АЯ,'_',09}

/*------------------------------------------------------------------*/

<следующая_альтернатива>

::= '||=' <альтернатива>

||= '|' <альтернатива>

/*------------------------------------------------------------------*/

<альтернатива>

::= <сентенция> <альтернатива_5>_

||= <альтернатива_5>

||= '*'

<альтернатива_5>

::= '(' <альтернатива> <* <следующая_альтернатива> *>_ ')'

<семантика>_ <сентенция>_

/*------------------------------------------------------------------*/

- 39 -

<сентенция>

::= <сентенция> (<обозначение_сентенции> |

<терминал>) <семантика>_ '+'_

||= <обозначение_сентенции> <семантика>_ '+'_

||= <терминал> <семантика>_ '+'_

/*------------------------------------------------------------------*/

<семантика> ::= '#{' < текст_на_языке_СИ > '}#'

< текст_на_языке_СИ >

::= !string{'}#'}

/*------------------------------------------------------------------*/

<обозначение_сентенции>

::= ('<*' <сентенция> '*>' string{09}_ |

'<' <нетерминальный_символ> '>' <степень>_

) '_'_

/*------------------------------------------------------------------*/

<терминал> ::= (<*<символ_5>*><пробельные_символы><степень>_

| '\'' + <*<символ_6>*> '\'' <степень>_

| '!' <спец_терминал>

| '[' <элементы>_']'

| '{' <элементы> '}'

| 'char' '{' <элементы>_ '}'

| 'string' '{' <элементы> '}'

) '_'_

<спец_терминал>

- 40 -

::= '[' <элементы>_']'

||= '{' <элементы> '}'

||= 'char' '{' <элементы>_ '}'

||= 'string' '{' <элементы> '}'

<пробельные_символы>

::= char{\11,\12,\15,\40}

||= *

/*------------------------------------------------------------------*/

<степень> ::= '^'string{09}_

/*------------------------------------------------------------------*/

<элемент> ::= <символ> <символ>_ <пробельные_символы>

||= '\''+ <*<символ_6>*> '\''

/*------------------------------------------------------------------*/

<элементы> ::= <элемент> <* ',' <элемент> *>_

/*------------------------------------------------------------------*/

<символ> ::= \\ + <управляющая_последовательность>

||= ! char{'\11','\12','\15','\40',',','\'',']','}'}+

/* любой символ ASCII кроме ' , ] } */

<символ_5> ::= \\ + <управляющая_последовательность>

||= ! char{'\11','\12','\15','\40',#,:,^,'\'',(,),*,

{,|,<}+

/* любой символ ASCII кроме

# : ^ ( ) * { | < */

<символ_6> ::= \\ + <управляющая_последовательность>

- 41 -

||= ! char{'\''} + /* любой символ ASCII кроме

' */

/*------------------------------------------------------------------*/

<управляющая_последовательность>

::= char{a,b,f,n,r,t,v,'\\','\'','\"','\?'}+

||= string{07}+

||= char{x,X} + string{09,AF} +

/*------------------------------------------------------------------*/

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