регистрация / вход

Алгоритм нисходящего разбора. Нисходящие распознаватели

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

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

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

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

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

мой грамматики. Именно это и является нашей задачей в данный момент.

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

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

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

тогда, когда это необходимо. Можно подобным способом определить разбор

справа налево, но он менее естественен. Инструкции в программе выполня-

ются слева направо, да и мы читаем слева направо.

Различают две категории алгоритмов разбора: нисходящий (сверху вниз)

и восходящий (снизу вверх). Их называют также разверткой и сверткой.

( В данном реферате будет рассмотрен процесс только нисходящего раз-

бора. ) Соотетственно, эти термины соответствуют и способу построения

синтаксического дерева. При нисходящем разборе дерево строится от корня

( начального символа ) вниз к концевым узлам. Метод восходящего разбора

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

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

предложение (1) в следующей грамматике целых чисел ( последовательностей,

состоящих из одной и более цифр ):

N ::= D | ND

D ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 (1)

На первом шаге непосредственный вывод N => ND будет строиться так,

как показано в первом дереве на рис. 1. На каждом последующем шаге

самый левый нетерминал V текущей сентенциальной формы xVy заменяется

на правую часть u правила V ::= u, в результате чего получается сле-

дующая сентенциальная форма. Этот процесс для предложения (1) предс-

тавлен на рис. 1. в виде пяти деревьев. Фокус в том, конечно, что

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

цепочкой.

N N N N N

| | | |

*-------* *-------* *-------* *-------*

| | | | | | | |

N D N D N D N D

| | | |

D D D 5

| |

3 3

N => N D => D D => 3 D => 3 5

Рис. 1. Нисходящий разбор и построение

вывода

2. Нисходящие разбор с возвратами

Алгоритм нисходящего разбора строит синтаксическое дерево, как уже

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

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

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

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

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

Чтобы свести осложнеия к минимуму, давайте опишем этот алгоритм раз-

бора образно. Вообразим, что на любом этапе разбора, в каждом узле уже

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

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

предложения.

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

го ему необходимо отыскать вывод Z => +x, где Z - начальный символ; сле-

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

Z => y где Z ::= y - правило. Пусть для Z существуют правила

Z ::= X X .. X | Y Y .. Y | Z Z .. Z

1 2 n 1 2 m 1 2 1

Сначала человек пытается определить правило Z ::= X X .. X . Если

1 2 n

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

нить второе правило Z ::= Y Y ... Y . В случае неудачи он переходит к

1 2 m

следующему правилу и т.д.

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

Z => X X .. X ? Заметим, что если вывод правилен, то для некоторых

1 2 n

цепочек x будет иметь место x=x x .. x , где X => *x для i=1,...,n.

i 1 2 n i i

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

M , который должен будет найти вывод X =>*x для любого x , такого,

1 1 1 1

что x = x ... Если сыну M удастся найти такой вывод, он (и любой из

1 1

сыновей, внуков и т.д.) закрывает цепочку x в предложении x и сообща-

1

ет своему отцу об успехе. Тогда его отец усыновит M , чтобы тот нашел

2

вывод X => *x , где x = x x ... и ждет ответа от него и т.д. Как толь-

2 2 1 2

ко сообщил об успехе сын M ,он усыновит еще и M , чтобы тот нашел

i-1 i

вывод X => *x . Сообщение об успехе, пришедшее от сына M , означает

i i n

что разбор предложения закончен.

Как быть, если сыну M не удается найти вывод X =>*x ? В этом

i i i

случае M сообщает о неудаче своему отцу; тот от него отрекается и

i

дает старшему брату M ,M такое распоряжение: "Ты уже нашел вывод,

i i-1

но этот вывод неверен. Найди-ка мне другой". Если M сумеет найти

i-1

другой вывод, он вновь сообщит об успехе, и все продолжится по-пре-

жнему. Если же M сообщит о неудаче, отец отречется и от него, и

i-1

тогда уже старшего брата M , попросят предпринять еще одну попыт-

i-2

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

1

вод Z => X X .. X был неверен, и человек, начинавший разбор, попы-

1 2 n

тается воспользоваться другим выводом Z => Y .. Y .

1 m

Как же действует каждый из M ? Положим, его целью является тер-

1

минал X . Входная цепочка имеет вид x=x x ..x T.. ,где символы в

1 2 i-1

x ,x ,...,x уже закрыты другими людьми. M проверяет, совпадает

1 2 i-1 i

ли очередной незакрытый символ T с его целью X . Если это так, он

i

закрывает этот символ и сообщает об успехе. Если нет, сообщает об

неудаче.

Если цель M - нетерминал X , то M поступает точно так же, как

1 1

и его отец. Он начинает проверять правые части правил, относящихся к

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

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

i

сообщает об успехе отцу. Если отец просит M найти другой вывод, а це-

i

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

i

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

i

младшего сына найти другой вывод и реагирует на его ответ также, как и

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

му отцу.

Теперь, наверное, понятно, почему этот метод называется прогнозиру-

ющим или целенаправленным. Используется и название "нисходящий" из-за

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

начального символа и нисходят к предложению (см рис. 2)

Z

|

*---*-------*

| | |

F | T

| | |

T |

| |

F |

| |

i + i * i

Рис. 2. Частичный нисходящий разбор предложения i+i*i.

Привлекательность этого метода (и его представления) в том и сос-

тоит, что каждый человек должен помнить лишь о своей цели, о своем от-

це, о своих сыновьях, а также о своем месте в грамматике и выходной це-

почке. И никому не нужны точные сведения о том, что происходит в других

местах. Это как раз и есть то, к чему мы вообще стремимся в программиро-

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

титься о собственной входной и выходной информации и ни о чем более.

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

ют стек типа LIFO (последний вошел - первый вышел), или, как его иногда

называют, "магазин".

Опишем алгоритм в более явном виде:

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

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

U ::= x|y|...|z

представлено, как Ux|y|...|z|$. То есть каждый символ занимает одну

ячейку, за каждой правой частью следует вертикальная черта "|", а за

последним символом следует "|$". Таким образом, грамматика

Z ::= E#

E ::= T+E|T

T ::= F*T|F

F ::= (E)|i

будет выглядеть как

ZE#|$ET+E|T|$TF*T|F|$F(E)|i|$

Каждый элемент стека соответствует человеку и состоит из пяти

компонент

(GOAL,i,FAT,SON,BRO)

которые означают следующее:

1. GOAL - цель, т.е. символ, который человек ищет. Таким обра-

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

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

передается ему отцом.

2. i - индекс в массиве GRAMMAR, указывающий на тот символ в

правой части для GOAL, с которым человек работает в данный момент.

3. FAT - имя отца (номер элемента стека, соответствующего от-

цу).

4. SON - имя самого последнего (младшего) из сыновей.

5. BRO - имя его старшего брата.

Нуль в любом из полей означает, что данная величина отсутствует.

В программе значение переменной v равно количеству участвующих в

разборе людей (количеству элементов в стеке в текущий момент), c -

имя (номер элемента в стеке) человека, работающего в данный момент.

Остальные ожидают конца его работы. Индекс j относитстя к самому ле-

вому (незакрытому) символу входной цепочки INPUT(1),...,INPUT(n).

а) Z б) СТЕКЦЕЛЬ i FAT SON BRO

|

*---------*------* 1 Z 4 0 15 0

| | 2 E 10 1 7 0

E # 3 T 20 2 4 0

| 4 F 28 3 5 0

*--*------* 5 i 0 4 0 0

| | | 6 + 0 2 0 3

T | E 7 E 12 2 8 6

| + | 8 T 18 7 12 0

F T 9 F 28 8 10 0

| | 10 i 0 9 0 0

i *---*---* 11 * 0 8 0 9

| | | 12 T 20 8 13 11

F * T 13 F 28 12 14 0

| | 14 i 0 13 0 0

i F 15 # 0 1 0 2

|

i

Рис 3. Стек после нисходящего разбора i+i*i

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

Поле SON используется для хранения ссылки на последнего (младше-

го) сына. Тогда поле BRO элемента, соответствующего этому сыну, укажет

на старшего брата. В качестве иллюстрации расмотрим изображенное на

рис .3 а) синтаксическое дерево для предложения i+i*i вышеописанной

грамматики. Состояние стека после окончания работы показано на рис.3 б).

Теперь у человека 2(S (2)) есть цель E; предполагается, что он в соотве-

тствии с синтаксическим деревом использует правило E ::= T+E. Таким

образом, ему для того, чтобы найти символы T,+,E потребуется три сына.

Значение поля S(2).SON=7, так что младшим сыном является человек, c

номером 7, цель которого E. Имя среднего сына - число 6, определяется

значением поля S(7).BRO; - цель этого сына - символ +. Имя старшего

сына находится в поле BRO человека 6 и равно 3.

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

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

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

дерева.

Рассмотрим теперь сам алгоритм нисходящего разбора. Для удобства

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

1. НАЧАЛЬНАЯ УСТАНОВКА

S(1) := (Z,0,0,0,0); c:=1; v:=1; j:=1; переход на НОВЫЙ ЧЕЛОВЕК

(первое усыновление. Цель усыновления - начальный символ Z.)

2. НОВЫЙ ЧЕЛОВЕК

IF GOAL терминал THEN Новый человек изучает свою цель.

IF INPUT (j)=GOAL THEN Цель - терминал.

BEGIN j:=j+1; Если GOAL совпадает с символом из

GO TO УСПЕХ; предложения, человек закрывает этот

ELSE GO TO НЕУДАЧА символ и сообщает об успехе.

i:= индекс в GRAMMAR правой Не совпадает - сообщает об неудаче.

части для GOAL; Цель нового человека - нетерминал.

GO TO ЦИКЛ Подготовка к просмотру правых частей

в правилах для GOAL

3. ЦИКЛ

IF GRAMMAR(i)="|" Просмотр правой части

THEN IF FAT=/=0 Достигли конца правой части, поэтому

THEN GO TO УСПЕХ сообщаем об успехе. Если нет отца,

ELSE STOP - предложение; то останов - окончен разбор

предложения

IF GRAMMAR(i )="$" Нет больше правых частей, которые

THEN IF FAT=/=0 можно было бы попробовать, поэтому

THEN GO TO НЕУДАЧА сообщение о неудаче или, если нет отца

ELSE STOP - не остановка, не распознав предложения

предложение;

v:=v+1; GRAMMAR(i) - другая цель, которую

S(v):=(GRAMMAR (i),0,c,0, можно попытаться найти. Берем сына.

SON); Тогда старший брат - тот, кто был до

этого младшим сыном

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

SON:=v; c:=v; и ждать от него ответа

GO TO НОВЫЙ ЧЕЛОВЕК

4. УСПЕХ

c:=FAT; Сообщить об успехе своему отцу. Он

i:=i+1; GO TO ЦИКЛ предпримет следующий шаг.

5. НЕУДАЧА

c:=FAT; Сообщить о неудаче своему отцу. Он

v:=v-1; отречется от сына и попросит его

SON:=S(SON).BRO; старшего брата предпринять еще одну

GO TO ЕЩЕ РАЗ попытку.

6. ЕЩЕ РАЗ

IF SON=0 THEN Есть ли еще сын, который может пред-

BEGIN WHILE GRAMMAR(i) принять еще одну попытку? Нет.

=/="|" Тогда пропускается правая часть -

DO i:=i+1; Это не та, которая нужна - переход к

i:=i+1 GO TO ЦИКЛследующей.

END;

i:=i-1; c:=SON; Естьсын. Его просят повторить попытку

IF GOAL нетерминал Его цель - нетерминал, так что он по-

THEN GO TO ЕЩЕ РАЗ пытается еще раз добиться успеха.

j=j-1 Его цель терминал. Попытка не приведет

GO TO НЕУДАЧА к успеху. Поэтому он открывает свой

символ и сообщает о неудаче.

Блок схема данного алгоритма приведена ниже.

*---------*

| 1 |

*----*----*

*---------------------------->| *------*

| * *----->| |<------*

| Нет / \ | | | |

| *-----------< 2 > | | * |

| Нет / \ А \ / | | Д / \ |

| *----------< 4 > | * | *-------< 9 > |

| | \ / | | | | | \ / |

| | * | | | | | * |

| | | Да | | Да | | | | Нет |

| | * | | | | | *---*---* |

| | *---* Н / \ | | | | | | 10 | |

| | | 6 |--< 5 > | * | | | *---*---* |

| | *---* \ / | / \ | | | | |

| | * | *-< 3 > | | | * |

| | | Да | | \ / | | | / \ Да |

| *-* | | | * | | | <1 1>-----*

*-|7| | | | *-----* | | \ /

*-* | | | Нет | | *

| *--|-------------* | | Нет

| | А | *---*---*

|<--------* | *--| 1 2 |

*---*---* | *-------*

| 8 |-------*

*-------*

Рис 4. Блок-схема алоритма нисходящего разбора

1. S(1) := (Z,0,0,0,0); c:=1; v:=1;

2. GOAL - терминал ?

3. j:=j+1; INPUT(j)=GOAL ?

4. GRAMMAR(i)="Конец" ?

5. FAT =/= 0 ?

6. STOP - Конецработы;

7. v := v+1; S(v) := (GRAMMAR (i),0,c,0,SON);

SON := v; c := v;

8. c := FAT; i := i+1;

9. SON = 0 ?

10. Пока GRAMMAR (i) =/= "Конец":

i := i+1,

j:=j+1;

i :=i -1;

c := SON;

11. GOAL - нетерминал ?

12. C := FAT ; v := v-1; SON := S (SON) * BRO.

3. Проблемы нисходящего разбора

Прямая левосторонняя рекурсия

В алгориме, описанном ранее, есть один серьезный недостаток,

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

ронней рекурсии. Если X - наша цель, а первое же правило имеет вид

X ::= X ..., то мы незамедлительно усыновляем того, кто будет искать

X. Он в свою очередь немедленно заведет себе сына, чтобы тот искал

X. Таким образом, каждый будет сваливать ответственность на своего сы-

на, и для решения задачи не хватит населения Китая.

По этой причине правила грамматики написаны с применением право-

сторонней рекурсии вместо более привычной левосторонней. Лучший способ

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

пользуя итеративные и факультативные обозначения. Запишем правила

(3.1) E ::= E+T | T как E ::= T { +T }

и

T ::= T*F | T/F | F как T ::= F { *F | /F }

Сейчас будут сформулированы два основных принципа, на основании

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

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

(3.2 ) Факторизация. Если существуют правила вида

U ::= xy | xw | ... |xz, то их надо заменить на

U ::= x(y|w|...|z), где скобки являются метасимволами

Допустима факторизация в более общей форме, такая как в арифметиче-

ских выражениях. Например, если в (3.2) y=y y и w=y w , мы могли бы за-

1 2 1 2

менить U ::= x (y|w|...|z) на

U ::= x(y (y |w )|...|z).

1 2 2

Заметим, что исходные правила U ::= x|xy мы преобразуем к виду

U ::= x(y|Л), где Л - пустая цепочка. Когда бы ни использовалось по-

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

ва, так как мы принимаем условие, что если цель - Л, то эта цель все-

гда сопоставляется.

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

рсию, использование этого приема сокращает размеры грамматики и позво-

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

После факторизации (3.2) в грамматике останется не более одной пра-

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

Если такая правая часть есть, мы делаем следующее:

(3.3) Пусть U ::= x|y|...|z|Uv - правила, у которых осталась леворе-

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

таксического класса U является x, y или z, за которыми либо ни-

чего не следует, либо следует только v. Тогда преобразуем эти

правила к виду U ::= (x|y| ... |z) {v}.

Мы использовали (3.3) чтобы сделать преобразование в (3.1),

позволяющее избавиться от ненужных скобок заключающихся в T. В качес-

тве другого примера преобразуем A ::= BC|BCD|Axz|Axy

а) Z б) Z

| |

*----*-* *-*-*-*-*-*-*

| | | | | | | | | |

E + T T + T + T + T

|

*--*-*

| | |

E + T

|

*-*-*

| | |

E + T

|

T

Рис 5. Деревья, использующие рекурсию и итерацию

Применив правило (3.2) получим A ::= BC(D|Л)|Ax(z|y); Применив

(3.3), получим A ::= BC(D|Л){x(z|y)}. Можно избавиться от одной па-

ры скобок, после чего получим A ::= BC(D|Л){x(z|y)}.

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

нисходящего разбора. Теперь алгоритм должен уметь обрабатывать альтер-

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

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

обрабатывать итерацию.

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

деревьев. Таким образом, рис 3.а должен был бы походить на рис. 3.б. Но

эти два дерева следует рассматривать как эквивалентные; операторы "плюс"

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

Общая левосторонняя рекурсия

Мы не решили всей проблемы левосторонней рекурсии: с прямой лево-

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

талась. Таким образом, правила

U ::= Vx и V ::= Uy|v

дают вывод U => +Uyx. Избавиться от этого не так просто, но обнаружить

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

левосторонней рекурсией. Символ U, получившейся в результате этих пре-

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

когда U FIRST+ U. Как проверить это отношение, нам уже известно.

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

Одной из проблем, возникающих при реализации нисходящих методов,

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

возможных представлений уже использовалось ранее. Очевидно, что оно

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

вующих каждому нетерминалу. Речь пойдет о другом представлении. Прежде

чем начать изложение, стоит упомянуть о том что написать конструктор,

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

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

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

лее форм довольно легко.

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

зываемая синтаксическим графом. Каждый узел представляет символ S из

правой части и состоит из четырех компонент: ИМЯ, ОПРЕДЕЛЕНИЕ (ОПР),

АЛЬТЕРНАТИВА (АЛТ) и ПРЕЕМНИК (ПРЕМ), где

1. ИМЯ - это сам символ S в некоторой внутренней форме.

2. ОПРЕДЕЛЕНИЕ равно 0, если S - терминал; в противном случае эта

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

перво правой части для S.

3. АЛЬТЕРНАТИВА указывает на первый символ той альтернативы пра-

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

узел (0, если такой правой части нет). Это только для символов

в правых частях.

4. ПРЕЕМНИК указывает на следующий символ правой части (0, если

такого символа нет).

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

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

правой части, относящейся к этому символу. Примером может служить

рис. 4, на котором изображено расположения компонент узла синтаксическо-

го графа грамматики:

*---------------------------*

| ИМЯ |

*--------*---------*--------*

| ОПР | АЛТ | ПРЕМ |

*--------*---------*--------*

Рис 6. Расположение компонент узла синтаксического

графа грамматики

Подробно о синтаксических графах см. в книге Д.Гриса "Конструи-

рование компиляторов для цифровых вычислительных машин"

Разбор без возвратов

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

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

полагаемая цель верна. Это нреобходимо потому, чтонам предстоит связать

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

находить цели, эти символы будут обрабатываться семантически. Вот неко-

торые примеры "обработки": 1) при обработке описаний переменных иденти-

фикаторы помещаются в таблицу символов; 2) при обработке арифметических

выражений проверяют, совместимы ли типы операндов.

Если возврат произошел из-за того, что прогнозируемая цель неверна,

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

время поисков этой цели. Сделать это не так -то просто, поэтому постара-

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

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

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

граммы. Тогда на грамматику налагается следующее требование: если есть

альтернативы x|y|...|z, то множества символов, которыми могут начинаться

выводимые из x,y,..,z слова, должны быть попарно различны. То есть если

x => *Au и y => *Bv то A =/= B. если это требование выполнено, можно

довольно просто определить, какая из альтернатив x,y или z - наша цель.

Заметим, что факторизация оказывает здесь большую помощь. Если есть пра-

вило U ::= xy|xz, ео преобразование этого правила к виду U ::= x(y|z)

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

ресекающимися.

4. Заключение

В данном реферате рассматривались нисходящие распознаватели,

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

разбором. Одна из первых статей, рассматривающих фиксированный ал-

горитм нисходящего разбора, принадлежит Айронсу. Но его метод не

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

ходящих и восходящих разборов. Алгоритм, приведенный в данном рефе-

рате, впервые был предложен в обзоре Флойда. Он же ввел и понятия

"отец - сын - брат". Способы избавления от возвратов описаны Унге-

ром.

5. Список литературы

1. Грисс. Конструирование компиляторов для цифровых вы-

числительных машин. М., Мир, 1975г.

2. Ахо А., Ульман Дж. Теория синтаксического анализа, пере-

вода и компиляции. М. Мир 1978г.

3. Ф.Льюис, Д.Розенкранц, Р.Стирнз. Теоретические основы

проектирования компиляторов. М., Мир, 1979г.

4. Фельдман Дж., Грис Д. Системы построения трансляторов.

Сб. Алгоритмы и алгоритмические языки, вып.5, ВЦ АН СССР, 1971г.

_

ОТКРЫТЬ САМ ДОКУМЕНТ В НОВОМ ОКНЕ

ДОБАВИТЬ КОММЕНТАРИЙ [можно без регистрации]

Ваше имя:

Комментарий