Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Lektsii_YaPiMT_ispravlennoe (2).docx
Скачиваний:
1
Добавлен:
01.03.2025
Размер:
342.66 Кб
Скачать

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

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

Представим, что на любом этапе разбора, в каждом узле уже построенной части дерева, находится по одному человеку. Люди, находящиеся в терминальных узлах, запоминают места, соответствующие символам предложений. Некому человеку предстоит провести разбор предложения Х. Следовательно ему необходимо отыскать вывод Z=>+x, z - начальный символ. Первым непосредственным выводом должен быть z=>y где z::=Y – правило. Пусть для z существуют правила z::=X1X2Xn|Y1Y2Ym|Z1Z2Zl.

Сначала человек пытается применить правило z::=X1X2Xn. Если нельзя построить дерево, используя это правило он делает попытку применить второе правило z::= Y1Y2Ym. В случае неудачи он переходит к следующему правилу и т.д. Как ему определить правильно ли он выбрал непосредственный вывод Z::=X1X2Xn? Если вывод правилен, то для некоторых цепочек xi будет иметь место х::=X1X2Xn, где Xi=>*xi i= . Прежде всего человек, выполняющий разбор, возьмёт себе «приёмного сына» М1, который должен найти вывод Х1=>*x1 x1такого, что х=х1 Если сыну М1 удаётся найти такой вывод, он ( и любой из его сыновей, внуков и .т.д.) закрывает цепочку х1 в предложении х и собирает своему от об успехе. Тогда его отец усыновляет M2, чтобы там нашёл вывод X2=>*x2, где х=х1х2 и ждёт ответа от него и т.д. Сообщение об успехе пришедшее от сына Mn, означает, что разбор предложения закончен.

Как быть, если сыну Мi не удаётся найти вывод Xi=>*xi ? В этом случае Mi сообщает об неудачи своему отцу, тот от него отрекается и даёт старшему брату Mi Mi-1 такое распоряжение: «Ты уже нашёл вывод, но этот вывод неверен. Найди другой». Если Mi-1 сумеет найти другой вывод, он вновь сообщает об успехе, и всё продолжается по прежнему. Если Mi-1 сообщает об неудаче, отец отречётся и от него, и тогда его уже старшего брата Mi-2, попросят предпринять ещё одну попытку. Если придётся отречься даже от М1, значит непосредственный вывод z X1X2Xn был неверен и человек, начинавший разбор, попытается воспользоваться другим выводом z::= Y1Y2Ym.

Как же действует каждый Mi? Пусть его целью является терминал Хi входная цепочка имеет вид x=x1x2xiТ…, где символы х1х2…хi-1 уже закрыты другими людьми. Mi проверяет, совпадает ли очередной незакрытый символ Т с его целью Хi. Если это так, он закрывает этот символ и сообщает об успехе; если нет – сообщает о неудаче.

Если цель Mi – нетерминал Хi, то Mi поступает точно также, как его отце. Он начинает проверять правые части правил, относящихся к нетерминалу, и, если необходимо, тоже усыновляет или отрекается от сыновей. Если все его сыновья сообщают об успехе, то Mi в свою очередь сообщает об успехе своему отцу. Если отец просит Mi найти другой вывод, а целью является терминальный символ, то Mi сообщает о неудаче, т.к. другого вывода не существует. В противном случае Mi просит младшего сына найти другой вывод и реагирует на его ответ также как и раньше. Если все сыновья сообщают о неудаче, он сообщает о неудаче своему отцу.

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

Привлекательность этого метода в том и стоит, что каждый человек должен помнить лишь о своей цели, о своём отце, о своих сыновьях, а также о своём месте в грамматике и во входной цепочке.

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

Для имитации усыновления и отречения от сыновей в программе используется стек LIFO. Пусть для реализации стека используется массив S и счётчик V. При V=0 стек пуст. При V=n, n>0 в стеке находится S[1]S[2]…S[n]. Предположим, что грамматика задана списком в одномерном массиве GRAMMAR таким образом, что каждое множество правил U::=x|y…|z представлено как Ux|y|…z|$, т.е. каждый символ занимает одну ячейку, за каждой правой частью U следует |, а за последней правой частью следует |$.

Таким образом грамматика:

Z::=E#

E::=T+E|T

T::=F*T|F

F::=(E)|i

Будет выглядеть ZE#|$ET+E|T|$TF*T|F|$F(E)|i|$.

Каждый элемент стека соответствует одному человеку и состоит из 5 компонент (GOAL,i,FAT,SON,BRO), которые означают следующие:

  1. GOAL – цель, т.е. символ, который человек ищет таким образом в незакрытой в данный момент части предположения ему предстоит найти такую голову, которая приводит к GOAL и закрыть её GOAL передаётся ему отцом.

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

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

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

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

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

С – имя(номер элемента в стеке) человека, работающего в данный момент. Остальные ожидают конца его работы. Индекс j относится к самому левому (незакрытому) символу входной цепочки. Если в программе встречаются обозначения GOAL, i, FAT, SON, BRО и нет других спецификаций, то считается, что они ссылаются на компоненты, относящиеся к тому человеку, который в данный момент работает, т.е. (C) -> S[с]. GOAL.

Можно хранить имена всех своих сыновей в собственном стеке, но в таком случае число полей элемента станет переменным. Но можно использовать поле SON для хранения ссылки на последнего (младшего) сына. Тогда поле BRО элемента соответствующего этому сыну, укажет на его старшего брата и т.д.

В качестве иллюстрации рассмотрим синтаксическое дерево для предложения i+i*i нашей грамматики.

стек

цель

i

FAT

SON

BRD

1

Z

4

0

15

0

2

E

10

1

7

0

3

T

20

2

4

0

4

F

28

3

5

0

5

i

0

4

0

0

6

+

0

2

0

3

7

E

12

2

8

6

8

T

18

7

12

0

9

F

28

8

10

0

10

i

0

9

0

0

11

*

0

8

0

9

12

T

20

8

13

11

13

F

28

12

14

0

14

i

0

13

0

0

15

#

0

1

0

2

Теперь у человека 2 – S[2] есть цель Е; предполагается, что он в соответствии с синтаксическим деревом использует правило Е::=Т+Е. ему для того, чтобы найти символы Т, +, и Е потребуется три сына. Значение поля S[2]. SON = 7, так что его младшим сыном является человек с номером 7, цель которого Е. Имя среднего сына – G, определяется значение поля S[7]. BRO, цель этого сына – символ ʺ+ʺ. Имя старшего сына находится в поле BRO человека и элементы этого списка в стеке связаны между собой стек в его окончательном виде – внутренняя форма синтаксическая дерева.

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

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