Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
shpora1_Kalabin.doc
Скачиваний:
2
Добавлен:
01.04.2025
Размер:
755.2 Кб
Скачать
  1. Порождающие грамматики Хомского. Примеры порождающих грамматик.

В 1956 г. Ноам Хомский предложил модель порождающей грамматики, которая весьма удобна для задания искусственных языков. Одно из удобств этой модели в том, что во многих случаях каждой порождаемой цепочке языка эта модель позволяет сопоставить ее структуру.

Определение 2.2. Порождающей грамматикой Хомского называется четверка объектов: G=(T,N,S,R), где: T – конечное непустое множество (терминальный словарь). Элементы T будем называть терминальными символами; N - конечное непустое множество (нетерминальный словарь). Элементы N будем называть нетерминальными символами; множества N и Т не пересекаются; S – выделенный элемент нетерминального словаря, S∈N, так называемый начальный символ; R – конечное непустое множество правил (продукций), каждое из которых имеет вид α→β, где и α, и β- цепочки над объединенным словарем T ∪N.

Пример 2.2. Рассмотрим следующую грамматику Хомского: G0=({a,b,c}, {S,A,B}, S, R), где: R={ S→aSbAc, aS→bbAc, bAc→B, SbA→ε, B→b }. Терминальный словарь грамматики G0 содержит три терминальных символа (или терминала) a, b и c, нетерминальный словарь содержит три нетерминальных символа (нетерминала) A, B и начальный нетерминал S, множество R содержит пять продукций (правил). Для того, чтобы различать в цепочках терминалы от нетерминалов, обычно (если не указано противное) терминалы обозначают строчными латинскими буквами a,b,c, …, а нетерминалы – прописными латинскими буквами A,B,C, … . Цепочки символов обычно обозначаются греческими буквами α, β, γ,... .При таком условии, если указать начальный нетерминал грамматики (по умолчанию будем считать начальным нетерминал S), то вся грамматика может быть задана перечислением конечного множества правил. Так, грамматика G0 может быть задана просто набором правил: G0= 1. S→aSbAc 2. aS→bbAc 3. bAc→B 4. SbA→ε 5. B →b

Рассмотрим теперь, как "работает" грамматика Хомского, каким образом можно, используя правила такой порождающей грамматики, получать цепочки языка. Во-первых, рассмотрим как из одних цепочек с помощью правил грамматики могут быть порождены другие цепочки. Это делается с помощью операции подстановки.

Определение 2.3. Из цепочки α непосредственно выводима цепочка β в грамматике G (обозначается α⇒Gβ, или просто α⇒β, если грамматика G очевидна), если: - цепочку  можно представить как конкатенацию трех цепочек, α=μτν (некоторые из цепочек могут быть пустыми); - цепочку β также можно представить как конкатенацию трех цепочек, β=μξν; - в грамматике G есть продукция τ→ξ ("разрешающая" подстановку цепочки ξ вместо подцепочки τ). Символ ⇒ обозначает бинарное отношение на множестве всех цепочек над объединенным словарем TUN. Пара цепочек α и βнаходится в этом отношении, если α⇒β. Это стандартное обозначение бинарного отношения. Таким образом, каждая грамматика Хомского G определяет бинарное отношение ⇒G на множестве (TUN)*.

Пример 2. 3. Из цепочки bAсaS в грамматике G0 можно непосредственно вывести несколько цепочек, в зависимости от того, какое правило подстановки использовать и какую подцепочку в исходной цепочке мы будем заменять. Например, bAсaS ⇒ ВaS, если по правилу 3 грамматики G0 вхождение подцепочки bAс мы заменим на цепочку В, bAсaS ⇒ bAcbbAc, если по правилу 2 грамматики G0 вхождение подцепочки aS заменяется на цепочку bbAc, bAсaS ⇒ bAcaaSbAc, если по правилу 1 грамматики G0 заменяется вхождение подцепочки S на цепочку aSbAc. Таким образом, грамматика Хомского представляет собой, фактически, конечное число правил α→β, где символ "→" можно интерпретировать как: "можно заменить на". Так из одних цепочек с помощью правил подстановки можно получить другие цепочки - объекты той же самой природы, что и исходные объекты. Поэтому операцию непосредственной подстановки можно выполнять многократно, используя каждую следующую полученную цепочку как исходную для ее дальнейшего преобразования.

Определение 2.4. Из цепочки α выводима цепочка β в грамматике G (обозначается α⇒*Gβ, или просто α⇒*β, если грамматика G очевидна), если существует конечное множество цепочек π0, π1, ... πn, n≥0, такое, что α=π0, πn= β, и для любых i= 1, ... n выполняется πi-1⇒πi. Иными словами, цепочка β выводима из цепочки α в грамматике G, если β можно получить из α за конечное число шагов применения операции непосредственной выводимости. Символ " ⇒* " означает рефлексивное транзитивное замыкание отношения

Пример 2. 4. Из цепочки bAсaS в грамматике G0 можно вывести несколько цепочек. Например (здесь выделены заменяемые подцепочки):

bAсaS ⇒ *cbc, поскольку bAсaS ⇒ BaS ⇒ caS ⇒ cbbAc ⇒ cbB ⇒ cbc;

bAсaS ⇒ * Baac, поскольку bAсaS ⇒ bAcaaSbAc ⇒ bAcaac ⇒ Baac.

Таким образом, грамматика Хомского представляет собой механизм порождения одних символьных цепочек из других символьных цепочек, причем из одной и той же цепочки можно породить несколько различных цепочек, иногда бесконечное их количество. Например, в грамматике G0 цепочка bAсaS после подстановки вместо S цепочки aSbAc по первому правилу превращается в цепочку, также содержащую S. Из этой новой цепочки по тому же правилу мы опять можем породить цепочку, содержащую S, и т.д.

Грамматика представляет возможность, инструмент, механизм порождения цепочек, и с этой точки зрения является исчислением, подобным, например, исчислению высказываний. Действительно, в исчислении высказываний объектами, над которыми производятся операции, являются высказывания, в грамматиках Хомского - цепочки символов. Конечным множеством операций в исчислении высказываний является полное множество логических операций {импликация, отрицание}, в грамматике Хомского – правила грамматики, в соответствии с которыми осуществляются подстановки. В отличие от исчисления высказываний, в грамматике Хомского множество операций уникально для каждой грамматики. Но в обоих случаях – и в исчислении высказываний, и в грамматиках – конечные множества операций используются для получения одних объектов из других: высказываний из высказываний, цепочек из цепочек. В обоих случаях порядок применения операций, алгоритм получения новых объектов из старых не предписан, можно использовать разные операции, разные объекты, и получать разные – обычно бесконечное число – порожденных объектов.

Перейдем теперь к вопросу о том, как связаны между собой порождающая грамматика Хомского и порождаемый ею язык. Как грамматика порождает язык?

Определение 2.5. Языком, порождаемым грамматикой G, называется множество терминальных цепочек, выводимых из начального символа грамматики. Или, формально, L(G) = {α∈Τ | S⇒G*α}.Итак, любой вывод цепочек языка мы должны начинать только с начального нетерминального символа. Если после произвольного числа подстановок подцепочек в соответствии с правилами грамматики, полученная в результате цепочка состоит из терминалов, то это – цепочка порождаемого грамматикой языка. (Отсюда ясны названия терминальные-нетерминальные символы. Только терминальные,'окончательные' символы могут встретиться в цепочках языка, заканчивающих вывод). Например, цепочка bbb принадлежит языку, порождаемому грамматикой G0. Действительно: S aSbAc ⇒ bbAcbAc ⇒ bBbAc ⇒ bBB ⇒ bbB ⇒ bbb.

Пример 2.5. Попробуем охарактеризовать весь язык, который порождается грамматикой G0. Начинаться любой вывод может только с начального нетерминала, и для его замены в G0 есть только одно, первое правило: S → aSbAc. Заменять S по первому правилу можно многократно, следовательно, мы в общем случае можем получить промежуточные цепочки вывода в этой грамматике вида: S⇒* anS(bAc)n. Дальше вывод может пойти двумя путями: либо мы используем второе правило aS → bbAc, для того, чтобы заменить подцепочку аS на цепочку, не включающую S, либо по четвертому правилу подцепочка SbA будет заменена на пустую цепочку (поскольку мы используем кроме S и соседние символы, по крайней мере один шаг в выводе должен быть сделан, т.е. n>0).

В первом случае имеем: S⇒* anS(bAc)n = an-1aS(bAc)n ⇒an-1bbAc(bAc)n⇒* an-1bBn+1⇒*an-1b n+2.Во втором: S⇒* anS(bAc)n = anSbAc(bAc)n-1 ⇒anc(bAc)n-1⇒* anсBn-1⇒*ancb n-1. Легко видеть, что никакие другие выводы из S в G0 невозможны.

Окончательно, L0=L(G0)={ an-1b n+2, ancb n-1 I n0 }.

  1. Классификация грамматик. Иерархия Хомского.

Ограничение типов правил, которые могут появляться в грамматике позволяет определить ряд специальных классов грамматик. Одна из стандартных классификаций известна как иерархия Хомского. Ее описывают следующим образом:

1. Любая грамматика определенного ранее вида – грамматика типа 0.

2. Если для всех правил вида α β, |α| |β|, где |α| и |β| – длина, т.е. число символов соответственно α и β, то грамматика называется грамматикой типа 1, или контекстно-зависимой (КЗ).

3. Если все левые части правил грамматики состоят из одного нетерминального символа, то это грамматика типа 2, или контекстно- свободная (КС).

4. Грамматика называется грамматикой типа 3, или регулярной, если каждое правило грамматики имеют одну из следующих форм. В случае грамматики, выровненной вправо, в правой части грамматики имеется не более одного нетерминала, который может быть только самым правым символом: A a, A aB

B случае грамматики, выровненной влево, в правой части грамматики имеется не более одного нетерминала, который может быть только самым левым символом:A a, A Ba

Иерархия – включающая, т.е. все грамматики типа 3 являются грамматиками типа 2 и т.д.

Иерархия грамматик соответствует иерархии языков. Например, если язык можно генерировать с помощью грамматики типа 2, то его называют языком типа 2. Необходимо помнить, что если для генерации языка можно использовать несколько грамматик, то тип языка соответствует грамматике с наибольшим типом.

Иерархия Хомского важна с точки зрения построения трансляторов с различных языков. Чем меньше ограничений в грамматике, тем сложнее ограничения, которые можно наложить на генерируемый язык. Чем более универсален класс используемой грамматики, тем больше свойств языка мы можем описать. Однако чем более универсальна грамматика, тем сложнее должна быть программа, распознающая строки соответствующего языка.

Грамматики типа 3 можно использовать для описания некоторых свойств языков программирования или высокоуровневых языков описания аппаратуры. Например, для генерирования идентификаторов по определению многих языков программирования можно воспользоваться следующими правилами:

I l,I l R, R l, R d, R l R, R d R, где буква (l) и цифра (d) обозначают терминалы (для краткости будем считать так, потому что перечисление всех возможных букв и цифр потребовало бы написания слишком большого числа правил). Иногда удобно объединять правые части правил, имеющих одинаковые левые части. Вышеприведенную грамматику можно также записать в виде:I l | l R, R l | d | l R | d R

Вертикальную черту здесь надо понимать как "или". Многие "локальные" средства языков программирования, например константы, ключевые слова языка и строки, представляются с помощью грамматик типа 3. Некоторые очень простые языки описания аппаратуры также можно описать с помощью регулярной грамматики. Однако грамматики типа 3 генерируют только строго ограниченные типы языков – регулярные выражения.

В алфавите А к регулярным выражениям относятся следующие:

1. Элемент А (или пустая строка). Если P и Q – регулярные выражения, то регулярными будут также и выражения

2. PQ (Q следует за P)

3. P | Q (P или Q)

4. P* (нуль или более экземпляров P)

В алфавите {a, b, c} ab* | ca* – регулярное выражение, которое описывает язык, включающий следующие строки (помимо прочих): abb c caaa ab ca

Пример регулярного выражения. Регулярное выражение, описывающее идентификатор, имеет вид: L ( L | D )*, где L обозначает букву, D – цифру.

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

Пример нерегулярного выражения. Рассмотрим язык, состоящий из строк открывающих и закрывающих скобок (плюс пустая строка), обладающих следующими свойствами:

1.) При чтении слева направо число встреченных закрывающих скобок

никогда не превышает число встреченных открывающих скобок.

2.) В каждой строке содержится одинаковое число открывающих и

закрывающих скобок. Например, следующая строка принадлежит языку: ( ) ( ( ) ( ) ( ( ) ) )

а приводимая ниже – нет:

( ( ( ) ( ) ) – не соответствует правилу 2.

Не существует способа представления этого языка с помощью регулярного выражения или его генерирования с помощью грамматики типа 3. Однако этот язык генерируется следующей контекстно-свободной грамматикой:

S (S), S SS, S ε

В большинстве языков программирования и языков описания аппаратуры имеются пары скобок, которые необходимо согласовывать, например: ( ) , [ ] , begin end каждой открывающей скобке должна соответствовать закрывающая.

Так begin () end – правильно; begin (end) – неправильно

Контекстно-свободная грамматика позволяет специфицировать подобные ограничения.

Как правило, большая часть синтаксиса языков программирования и специализированных языков САПР описывается с помощью КС-грамматики. Однако у большинства языков есть некоторые свойства, которые нельзя выразить с помощью КС-грамматики. Например, присваивание X:=Y может быть допустимым, если объявлено, что X и Y имеют соответствующие типы, или недопустимым при несоответствии типов. Условие такого вида не может быть специфицировано КС- грамматикой, и компиляторы обычно выполняют проверку типа не на фазе формального синтаксического анализа. Однако идею КС-грамматики можно расширить, включив некоторые контекстно-зависимые свойства языков.

Двухуровневые грамматики (W-грамматики, названной так в честь их изобретателя – А. ван Вейнгаардена). Идея применения двухуровневой грамматики состоит в том, что если правила обычной грамматики обеспечивают конечный способ описания языка, состоящего из бесконечного числа строк, то здесь вторая грамматика применяется для генерирования бесконечного числа правил, которые в свою очередь генерируют предложения языка. Применение второй грамматики позволяет избежать рутинной работы, связанной с написанием бесконечного числа порождающих правил. С помощью двухуровневой грамматики можно генерировать любой язык типа 0. Данная концепция является даже слишком мощной (КЗ свойства большинства машинных языков относительно просты).

Атрибутивные грамматики. Атрибуты применяются для описания КЗ (точнее К-несвободных) аспектов языка. Рассмотрим пример. Пусть в некотором языке идентификаторы могут иметь тип int или char и являются терминалами грамматики. Описание с помощью КС порождающих правил. D → int I, D → char I

Описав идентификатор, мы хотим запомнить его тип. Этот тип будет свойством описания, видоизменим грамматику, чтобы это указать: D (.ID, MODE) → int (.MODE1) I(.ID1), ID = ID1, MODE = MODE1

аналогично для char. MODE, MODE1, ID, ID1 пишутся в скобках после терминала или нетерминала грамматики и представляют собой его признаки (атрибуты). Преимущество атрибутивных грамматик в том, что они выглядят как КС, но могут специфицировать КЗ конструкции языка.

Фактически любой язык типа 0 можно описать с помощью атрибутивной грамматики. Так как языки программирования представляется как КС, к которым добавляются контекстные ограничения, атрибутивные грамматики хорошо подходят для их описания.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]