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

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

 22.1. Определения и общие свойства порождающих грамматик

Упомянутые выше нормальные формы Бэкуса и синтаксические

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

отчасти, семантики. Приведем пример описания понятия "иденти-

фикатор" с использованием формализма БНФ:

<идентификатор> ::= <буква> │ <буква> <буквы и цифры>

<буквы и цифры> ::= <буква> │ <цифра> │

<буква> <буквы и цифры> │

<цифра> <буквы и цифры>

<буква> ::= a │ b │ c │ d │ e │ f │ g │ h │ i │ j │ k │ l │ m

│ n │ o │ p │ q │ r │ s │ t │ u │ v │ w │ x │ y │ z

<цифра> ::= 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9

- 19 -

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

талингвистическими переменными или нетерминальными символами.

В этих обозначениях проявляется семантика конструкций языка

программирования.

Запись вида "::=" читается как "это есть". Вертикальная

черта задает перечисление альтернативных определений данного

понятия. Оставшиеся символы называются терминальными.

Позже стали использоваться так называемые расширенные БНФ

НЕ нашли? Не то? Что вы ищете?

(РБНФ). Их отличие в том, что в правых частях правил было раз-

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

еся терминальными. Квадратные скобки обозначают необязательный

элемент, а фигурные задают возможное повторение нуль или более

раз. Приведенный выше пример на РБНФ выглядит так:

<идентификатор> ::= <буква> {<буква или цифра>}

<буква или цифра> ::= <буква> │ <цифра>

<буква> ::= a │ b │ c │ d │ e │ f │ g │ h │ i │ j │ k │ l │ m

│ n │ o │ p │ q │ r │ s │ t │ u │ v │ w │ x │ y │ z

<цифра> ::= 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9

В синтаксических диаграммах терминальные и нетерминальные

символы различаются внешним видом контура: терминальные эле-

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

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

записываться в синтаксических диаграммах без контура. Название

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

Пути, ведущие от начала диаграммы к ее концу задают все син-

таксически корректные варианты. На рисунке 5 приведены диаг-

раммы понятий "идентификатор", "буква" и "цифра".

Нормальные формы Бэкуса, а вместе с ними и синтаксические

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

тики. Для этого металингвистические переменные заменяются не-

терминальными символами грамматики, комбинация "::=" заменя-

ется стрелкой, терминальные символы остаются неизменными, а

каждой альтернативе соответствует свое правило грамматики.

- 20 -

 ш1

┌─────┐

┌<─┤буква├─┐

Идентификатор ┌─────┐ │ └─────┘ │

─────────────>│буква├──┼──────────┼───────────────>

└─────┘ │ ┌─────┐ │

└<─┤цифра├─┘

└─────┘

┌──── a ────┐ ┌──── 0 ────┐

│ │ │ │

├──── b ────┤ ├──── 1 ────┤

│ │ │ │

Буква ││ Цифра ││

─────────┤ ├────> ───────┤ ├────>

│ │ │ │

└──── z ────┘ └──── 9 ────┘

Рис. 5. Пример синтаксических диаграмм

 ш0

Дадим формальное определение грамматики [2].

 2Определение 0. Порождающей грамматикой G называется четверка

объектов: <Vt, Vn, S,P>, где

Vt - конечное множество терминальных символов;

Vn - конечное множество нетерминальных символов, причем

множества Vt и Vn не пересекаются;

S - начальный символ грамматики, принадлежащий множеству

Vn;

P - множество правил вывода вида x -> y, где x и y цепоч-

ки терминальных и нетерминальных символов.

Здесь и в дальнейшем будем придерживаться следующих обоз-

начений: малые латинские буквы из начала алфавита будут обоз-

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

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

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

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

вать индексы.

 2Определение 0. Говорят, что цепочка x1 непосредственно вы-

водима из цепочки x0 в грамматике G (записывается так:

x0->/G/->x1 ), если цепочки x0, x1 представимы в виде

x0=w1yw2, x1=w1zw2, и среди правил вывода есть правило y->z.

 2Определение 0. Если в грамматике G есть непосредственная

выводимость x[0]->x[1], x[1]->x[2], ..., x[n-1]->x[n], то го-

ворят, что цепочка x[n] выводима из x[0] в грамматике G (за-

писывается так: x[0]->/+G/->x[n]). Символ грамматики можно

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

- 21 -

В частных случаях, x[0] может быть нетерминалом или тер-

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

нетерминала, выводимость терминальной цепочки.

 2Определение 0. Множество терминальных цепочек, выводимых в

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

даемым этой грамматикой, и обозначается L(G).

 2Пример.  0Рассмотрим следующую грамматику G:

1) Vt = { a, b, c, 0, 1, 2 },

2) Vn = { A, B, C, D },

3) начальный символ грамматики - A,

4) правила вывода S = { A -> BC; B -> a; B -> b; B -> c;

C -> B; C ->D; C -> CC; D -> 0; D -> 1; D -> 2 }.

В язык L(G), порождаемый данной грамматикой G, входят це-

почки, начинающиеся с одной из букв: a, b или c. Далее следуют

один или несколько буквенных (a, b, c), или цифровых (0, 1, 2)

символов. Покажем, как, например, выводится цепочка ab0с:

A -> BC -> aC -> aCC -> aCCC -> aBCC -> abCC -> abDC -> ab0C

-> ab0c. Перечислить все цепочки языка невозможно, так как их

количество бесконечно.

Одной из основных проблем, связанных с практическим при-

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

ния. Для конкретной грамматики алгоритмическая разрешимость

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

шагов проверить принадлежность произвольной терминальной це-

почки языку, порождаемому данной грамматикой. Очевидно, что

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

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

Описанный выше язык L(G) является распознаваемым.

Действительно, рассмотрим произвольную цепочку символов над

терминальным алфавитом Vt. Пусть ее длина равна l. Если l<2,

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

Пусть l>=2, тогда применим к начальному символу правило

вывода A -> BC. Далее применим (l-1) раз правило C -> CC. В

результате получим цепочку нетерминалов: BCC...C. Принадлеж-

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

символов из соответствующего нетерминала. Последнее легко про-

верить полным перебором правил вывода.

 2Определение 0. Количество символов, составляющих цепочку,

называется длиной цепочки (обозначение: l(x) или │x│ ). Если

- 22 -

l(x)=0, то цепочка называется пустой. Будем обозначать пустую

цепочку символом -  7e 0.

 2Определение 0. Грамматика называется неукорачивающей, если

для любого правила вывода левая часть не длиннее правой.

 2Теорема 0. Пусть G - неукорачивающая грамматика, тогда L(G)

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

распознавания есть функция длины цепочки [2].

 2Указание 0. Доказательство основывается на полном переборе

бесповторных выводов.

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

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

приводит к потере естественной интерпретации нетерминальных

символов как обозначений (имен) синтаксических конструкций.

 22.2. Классификация грамматик

Существует несколько вариантов классификации порождающих

грамматик. Ниже приводится вариант классификации по Хомскому

[11]. Грамматики перечисляются в следующем порядке: произволь-

ные грамматики, неукорачивающие грамматики, грамматики не-

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

укорачивающие контекстно-свободные грамматики, автоматные

грамматики.

1)  2Произвольные грамматики 0 - по Хомскому -  2тип 0 0.

2)  2Неукорачивающие грамматики 0 -  2тип 1 0.

3)  2НС - грамматики 0 (вид правил: xAy -> xty, t<>  7e 0 ).

Класс НС-грамматик эквивалентен классу неукорачивающих

грамматик (следовательно, по Хомскому тоже  2тип 1 0). Другое наз-

вание НС-грамматик - контекстно зависимые грамматики.

4)  2КС - грамматики  0(вид правил: A -> x, x<>  7e 0) - по

Хомскому -  2тип 2 0. Класс КС-языков уже класса НС-языков.

5)  2УКС - грамматики  0(вид правил: A -> x, где x, возмож-

но, пустая цепочка). По любой УКС-грамматике можно построить

- 23 -

почти эквивалентную ей КС-грамматику (тот же язык за исключе-

нием, возможно, пустой цепочки).

6)  2А - грамматики. 0 Лево и правоавтоматные или иначе - ре-

гулярные грамматики (вид правил: A -> aB, A -> a). По Хомскому

-  2тип 3 0.

 2Упражнение. 0 L={xbx}. (В терминальной цепочке "x" терминал

"b" не присутствует.) Показать, что это НС-язык.

(Ответ: I->b; I->IA[i]a[i]; a[i]A[j]->A[j]a[i]; bA[i]->a[i]b)

 2Пример.  0Рассмотрим скобочный язык. Он состоит из беско-

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

структурой. Порождающая его грамматика очень проста:

1) E -> ( )

2) E -> ( E )

3) E -> E E

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

Однако, пример КС-грамматики не означает, что не существует

грамматики с более жесткими ограничениями (в данном случае -

регулярной).

Докажем, что скобочный язык действительно не является ре-

гулярным.

Предположим противное: пусть существует регулярная грам-

матика (например, левоавтоматная). Тогда, правила вывода имеют

вид: A -> a, A -> aB. В качестве терминальных символов будут

выступать левые и правые скобки.

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

символов. Рассмотрим скобочную цепочку, состоящую из k левых

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

скобочная цепочка. Построим вывод этой цепочки в нашей гипоте-

тической грамматике.

На каждом шаге вывода порождается один терминальный сим-

вол, поэтому всего будет 2k шагов. При этом, на каждом шаге в

получаемой цепочке будет присутствовать ровно один нетерминал.

Рассмотрим первую половину этого вывода (порождение k левых

скобок). Очевидно, что среди использованных в этой части выво-

да нетерминалов найдутся хотя бы два совпадающих, то есть:

E => ... => x 2Z 0 => ... => y 2Z 0 => t

- 24 -

Здесь x и y некоторые терминальные цепочки, а t - результирую-

щая цепочка. Пусть выделенная часть вывода содержит m шагов,

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

скобок.

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

другой вывод, отличающийся от исходного тем, что в первой его

части будет выброшена выделенная ранее часть. И, следователь-

но, нашему языку будет принадлежать цепочка из (k-m) левых

скобок и k правых скобок. А это запрещено. Полученное противо-

речие означает, что не может существовать регулярная граммати-

ка, порождающая скобочный язык.

 23. ВЫВОД ЦЕПОЧЕК

 23.1. Задача разбора

Мы рассмотрели задачу порождения цепочек языка. Однако,

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

вать их структуру. Эта задача называется задачей разбора.

Структура цепочки может быть задана графически в виде дерева.

В программе часто применяют иной способ - линейно скобочная

запись.

 2Пример. 0 Пусть даны правила вывода порождающей грамматики

G с начальным символом E:

1) E -> E + T

2) E -> T

3) T -> T * F

4) T -> F

5) F -> ( E )

6) F -> a

7) F -> b

Здесь E - арифметическое выражение; T - терм; F - множи-

тель.

Рассмотрим цепочку (a+b)*a. Очевидно, что она принадле-

жит языку L(G). Попробуем построить вывод этой цепочки из на-

чального символа: E -> T -> T*F. На этом шаге возникает неоп-

ределенность: к какому нетерминалу применять следующее правило

- 25 -

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

раз применяется к самому левому вхождению нетерминала. Тогда

последовательность вывода имеет вид:

E -> T -> T*F -> F*F -> (E)*F -> (E+T)*F -> (T+T)*F ->

-> (F+T)*F -> (a+T)*F -> (a+F)*F -> (a+b)*F -> (a+b)*a

Мы осуществили так называемый левосторонний вывод. Пе-

речиcлим номера правил: 2,3,4,5,1,2,4,6,4,7,6. Последователь-

ность порождающих правил левостороннего вывода называется ле-

восторонним или нисходящим (от начального символа к терминаль-

ной цепочке) разбором.

Обратите внимание: в нашем случае существует только один

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

левого нетерминала существует единственное правило, ведущее к

результирующей терминальной цепочке.

Кроме левостороннего существуют правосторонние и смешан-

ные выводы.

Под правосторонним или восходящим (от терминальной цепоч-

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

вательность порождающих правил при правостороннем выводе. В

нашем примере с цепочкой (a+b)*a правосторонний вывод соот-

ветствует последовательности номеров: 2,3,6,4,5,1,4,7,2,4,6; а

правосторонний разбор - это номера: 6,4,2,7,4,1,5,4,6,3,2.

Позже будет показана связь левостороннего и правостороннего

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

 23.2. Дерево разбора

Найденный вывод цепочки можно представить в виде дерева,

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

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

6 приведено дерево для цепочки из предыдущего примера. Обрати-

те внимание, в дереве не отражен порядок применения правил вы-

вода; то есть всем возможным выводам соответствует одно дере-

во, поэтому дерево называется деревом разбора. Структура дере-

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

ми обозначены применяемые правила.

- 26 -

 ш1

E

│ 2

T

│ 3

┌─────────────┼─────────────┐

│ │ │

T * F

│ 4 │ 6

│ │

F a

│ 5

┌─────────┼────────┐

( E )

│ 1

┌─────┼─────┐

E + T

│ 2 │ 4

T F

│ 4 │ 7

F b

│ 6

a

Рис. 6. Пример дерева вывода

 ш0

Теперь видно, что левосторонний разбор соответствует

построению дерева разбора в порядке сверху вниз. Правосторон-

ний разбор эквивалентен построению дерева снизу вверх.

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

линейно-скобочной записи:

(E (T (T (F '(' (E (E (T (F 'a'))) '+'

(T (F 'b'))) ')') * (F 'a')))

Принцип построения этой записи заключается в следующем.

Выписываем в скобках символ корня дерева и символы вершин сле-

дующего уровня. Если эти вершины являются корнями поддеревьев,

то для них выполняем ту же процедуру.

Оказывается, существуют грамматики, допускающие построе-

ние различных деревьев разбора для одной и той же цепочки. Та-

кие грамматики называются синтаксически неоднозначными. Языки,

все порождающие грамматики которых синтаксически неоднозначны,

называются существенно неоднозначными.

.

- 27 -

 2Упражнение.  0Покажите, что следующая грамматика является

синтаксически неоднозначной:

1) S -> S + S

2) S -> a

Известна интерпретация построения дерева вывода, напоми-

нающая игру в домино. Эта интерпретация принадлежит Де Ремеру.

Она заключается в следующем [11].

Каждому правилу вывода соответствует кость домино, у ко-

торой одна сторона представлена половинкой квадрата с надписью

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

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

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

порядком следования соответствующих символов в правой части

правила вывода.

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

щим нетерминальным или терминальным символом. Считается, что

эти половинки и квадраты соединены эластичными нитями с поло-

винкой нетерминала левой части. Количество таких костей опре-

деляется потребностью игрока (потенциально их бесконечно мно-

го).

В начале игры на игровом поле расположена половинка квад-

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

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

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

стыкуются только одинаково помеченные половинки.

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

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

данную в начале игры терминальную цепочку.

На рисунке 7 приведен пример размещения костей домино для

построения терминальной цепочки "a*b" (для грамматики из пунк-

та 3.1)

.

- 28 -

 ш1

┌─────────┐

│ E │

├─────────┤

│ E │

└────┬────┘

┌────┴────┐

│ T │

├─────────┤

│ T │

└────┬────┘

┌──────────────┼───────────────┐

┌────┴────┐ │ ┌────┴────┐

│ T │ │ │ F │

├─────────┤ │ ├─────────┤

│ T │ │ │ F │

└────┬────┘ │ └────┬────┘

┌────┴────┐ │ │

│ F │ │ │

├─────────┤ │ │

│ F │ │ │

└────┬────┘ │ │

┌────┴────┐ ┌────┴────┐ ┌────┴────┐

│ │ │ │ │ │

│ a │ │ * │ │ b │

│ │ │ │ │ │

└─────────┘ └─────────┘ └─────────┘

 ш0

Рис. 7. Пример игры Де Ремера

 23.3. Классификация методов разбора

Методы разбора делятся на нисходящие, восходящие и сме-

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

разбора.

Отказ от принятого решения в процессе разбора цепочки на-

зывается возвратом.

Методы разбора делятся на детерминированные (без возвра-

тов) и недетерминированные (с возвратами). Недетерминированные

методы более дорогостоящи по сравнению с детерминированными с

точки зрения расхода времени и памяти. Сложность при осущест-

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

рых ранее выполненных действий. Конечно, здесь удобны ре-

курсивные алгоритмы, которые, однако, в свою очередь не очень

эффективны.

.

- 29 -

 24. ЛЕКСИЧЕСКИЙ АНАЛИЗ

 24.1. Регулярные выражения

Лексический анализ - это первая фаза компиляции. На этом

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

лы. Примеры таких символов: идентификаторы, служебные слова,

числа, разделители. В однопроходных трансляторах лексический

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

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

анализ как отдельную фазу.

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

помощью регулярных выражений.

 2Определение.  0Регулярное выражение задается следующим об-

разом [11]:

 21 0) любой символ исходного алфавита или пустая цепочка -

это регулярное выражение,

 22 0) если P и Q - регулярные выражения, то PQ (за P следует

Q - операция конкатенации) - это регулярное выражение,

 23 0) если P и Q - регулярные выражения, то P│Q (P или Q -

операция выбора) - это регулярное выражение,

 24 0) если P - регулярное выражение, то P* (нуль или более

экземпляров из P. Здесь "*" - операция повторения) - это регу-

лярное выражение.

Приоритеты (в порядке убывания): повторение, конкатена-

ция, выбор.

Например, вещественное число в форме без порядка может

быть представлено формулой:

(+│-│)dd*.dd* , где d - произвольная цифра.

По регулярному выражению легко написать регулярную (авто-

матную) грамматику.

Автоматная грамматика называется регулярной, если в ней

нет правил вывода вида: S->aS, где S - начальный символ грамма-

тики. Легко заметить, что любая автоматная грамматика без тру-

да трансформируется в регулярную введением нового начального

символа.

.

- 30 -

Для предыдущего выражения из предыдущего примера регуляр-

ная грамматика будет следующей (здесь под номерами 4, 5, 7 и 8

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

десять правил с соответствующими десятью цифрами):

1) R -> + S 5) T -> d T

2) R -> - S 6) T -> . V

3) R -> S 7) V -> d

4) S -> d T 8) V -> d V

Существует полное соответствие между регулярными выраже-

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

ными автоматами, описанными ниже.

 24.2. Конечные автоматы

Приведем определение детерминированного конечного автома-

та [2].

 2Определение. 0 Под конечным автоматом понимается совокуп-

ность следующих пяти объектов:

1) K - конечное множество состояний,

2) A - конечный алфавит,

3) S - начальное состояние (из множества K),

4) F - множество заключительных состояний (подмножество K).

5) D: K*A -> K - частично определенная функция переходов.

Говорят, что  2конечный автомат принимает  0(допускает) вход-

ную цепочку, если при ее анализе, начиная с начального состоя-

ния, функция D определена на каждом шаге, и последнее состоя-

ние автомата является заключительным.

Говорят, что  2конечный автомат не принимает 0 (не допускает)

входную цепочку, если или последнее состояние автомата не яв-

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

функция D.

Автомат называется недетерминированным, если отображение

D многозначно, то есть на всех или некоторых наборах парамет-

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

образом. Можно показать, что по всякому недетерминированному

- 31 -

автомату можно построить эквивалентный ему (то есть, допускаю-

щий тот же язык) детерминированный автомат.

 2Определение.  0Входная цепочка допускается недетерминиро-

ванным конечным автоматом, если существует такой порядок при-

менения отображения D, при котором по окончанию входной цепоч-

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

случае говорят, что автомат не допускает входную цепочку.

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

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

обозначим через ' '. Для этого определим все составные части

автомата:

1) K = {0,1}

2) A = {a, b, ..., z, 0, 1, ..., 9, ' '}

3) S = 0

4) F = {1}

5) Функцию переходов зададим таблично (см. табл. 1). Си-

туацию недопустимого символа обозначим словом fail.

 2Таблица 1

 2Таблица переходов

 ш1

┌─────┬─────────┬─────────┬─────┐

│K \ A│ a,...,z │ 0,...,9 │ ' ' │

├─────┼─────────┼─────────┼─────┤

│ 0 │ 1 │ fail │ 0 │

├─────┼─────────┼─────────┼─────┤

│ 1 │ 1 │ 1 │fail │

└─────┴─────────┴─────────┴─────┘

 ш0

Удобно задавать конечный автомат графом (диаграммой пере-

ходов). В этом случае узлы графа соответствуют состояниям, а

направленные дуги помечаются символами. Для предыдущего приме-

ра граф представлен на рисунке 8.

 ш1

┌───┐ a, b, ..., z ┌───┐

┌─────>│ 0 ├──────────────────>│ 1 │<──────────┐

│ └─┬─┘ └─┬─┘ │

│ │ │ │

│ ' ' │ │ 0, ..., 9 │

└────────┘ │ a, ..., z │

└─────────────┘

 ш0

Рис. 8. Пример графа переходов

- 32 -

Можно показать, что по любому недетерминированному конеч-

ному автомату строится детерминированный автомат, распознающий

(допускающий) тот же язык.

 2Упражнение.  0Покажите эквивалентность способов задания ре-

гулярного языка: лево - и право - автоматные грамматики, детер-

минированный и недетерминированный конечные автоматы, регуляр-

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

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

способов).

 24.3. Лексический анализатор

Написание лексического анализатора заключается в модели-

ровании различных автоматов для распознавания различных симво-

лов. В качестве примера рассмотрим задачу распознавания ве-

щественного числа в форме без порядка. Соответствующий конеч-

ный автомат представлен диаграммой (графом) переходов на

рисунке 9. Под стрелками указаны допустимые символы. Вещест-

венное число считается принятым конечным автоматом, если авто-

мат перейдет в четвертое состояние. Символ d обозначает как и

прежде произвольную цифру.

Например, строка "314.1Е-02" считается правильной записью

вещественной константы до момента чтения символа "Е" (автомат

последовательно находится в состояниях: ), но так

как при этом строка не исчерпана, то полная запись "314.1Е-02"

с точки зрения данного автомата считается некорректной.

Второй пример: строка "+.343" считается некорректной, так

как символ "." недопустим после знака "+".

 ш1

┌─────────────────┐

│ │

┌───┴───┐ ┌───────┐│d ┌───────┐ ┌───────┐ ┌───────┐

│ 0 ├───>│ 1 │└──>│ 2 ├───>│ 3 ├───>│4(закл)│

│началь-│+/- │чтение ├───>│чтение │ . │чтение │ d │чтение │

│ное со-│ │знака │ d │целой │ │точки │ │дробной│

│стояние│ │числа │┌──>│части │ │ │┌──>│части │

└───────┘ └───────┘│ └───┬───┘ └───────┘│ └───┬───┘

│ d │ │ d │

└───────┘ └───────┘

 ш0

Рис. 9. Граф переходов (пример вещественного числа)

- 33 -

Ниже приведен текст процедуры-функции, реализующей

распознавание числа.

function Read_Real:boolean;

const final=[4];

var s : 0..4;

ch : char;

{ Error - внешняя процедура обработки ошибок }

begin

s:=0;

while not eoln do

begin

read(ch);

if ch in ['+','-','0'..'9','.'] then

case s of

0 : if (ch='+') or (ch='-') then s:=1

else if ch in ['0'..'9'] then s:=2

else Error;

1 : if ch in ['0'..'9'] then s:=2

else Error;

2 : if ch='.' then s:=3

else if not(ch in ['0'..'9']) then Error;

3 : if ch in ['0'..'9'] then s:=4

else Error;

4 : if not(ch in ['0'..'9']) then Error;

end

end;

Read_Real:= (s in final)

end;

Обратите внимание, что приведенная программа "дословно"

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

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

В приложении 1 приведен текст программы, включающей ска-

нер для распознавания лексем: целое число (возможно со зна-

ком), знак операции, конец строки.

.

- 34 -

 24.4. Пример сканера для языка PL/0

В качестве примера лексического анализатора рассмотрим

сканер Вирта [3], предназначенный для распознавания лексем

специального учебного языка PL/0. В приложении 2 приведены

синтаксические диаграммы этого языка.

Сканер оформлен в виде процедуры Getsym и выполняет сле-

дующие действия:

1) пропускает пробелы,

2) распознает разделители,

3) распознает служебные слова,

4) распознает незарезервированные слова как идентификато-

ры,

5) распознает последовательности цифр в качестве целых

чисел (значение числа присваивается переменной num),

6) распознает знак присваивания ':='.

Внутри процедуры Getsym описана локальная процедура чте-

ния очередной литеры : Getch. В тексте используется внешняя

процедура обработки ошибок: Error.

Для хранения служебных слов учебного языка PL/0 предназ-

начен массив word. Дополнительно, в массиве wsym хранятся типы

(символы), приписанные служебным словам, а в массиве ssym хра-

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

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

Const

norw=11; {количество служебных слов}

txmax=100; {длина таблицы имен}

nmax=14; {максимальное количество цифр в записи числа}

al=10; {максимальная длина идентификатора}

Из за большого объема этот материал размещен на нескольких страницах:
1 2 3 4 5