Построение FIRST и FOLLOW — различия между версиями
Shersh (обсуждение | вклад) (добавлены леммы FOLLOW) |
Shersh (обсуждение | вклад) (добавлен псевдокод FOLLOW) |
||
| Строка 27: | Строка 27: | ||
changed = ''true'' | changed = ''true'' | ||
'''while''' changed | '''while''' changed | ||
| − | changed = '' | + | changed = ''false'' |
'''for''' <tex>( A \to \alpha \in P )</tex> | '''for''' <tex>( A \to \alpha \in P )</tex> | ||
<tex> \mathrm{FIRST}[A]\ \cup =\ \mathrm{FIRST}(\alpha) </tex> | <tex> \mathrm{FIRST}[A]\ \cup =\ \mathrm{FIRST}(\alpha) </tex> | ||
| Строка 67: | Строка 67: | ||
|statement=Для каждого правила вида <tex> A \to \alpha B </tex> или <tex> A \to \alpha B \beta,\ \varepsilon \in \mathrm{FIRST}(\beta)</tex> верно, что <tex>\mathrm{FOLLOW}(A) \subset \mathrm{FOLLOW}(B) </tex> | |statement=Для каждого правила вида <tex> A \to \alpha B </tex> или <tex> A \to \alpha B \beta,\ \varepsilon \in \mathrm{FIRST}(\beta)</tex> верно, что <tex>\mathrm{FOLLOW}(A) \subset \mathrm{FOLLOW}(B) </tex> | ||
}} | }} | ||
| + | === Псевдокод === | ||
| + | Реализация построения <tex> \mathrm{FOLLOW} </tex> получается сразу из [[#lemmafollow1 | лемм]]. Для алгоритма сначала требуется выполнить построение <tex> \mathrm{FIRST} </tex> для грамматики. | ||
| + | <code> | ||
| + | '''function''' constructFOLLOW(): | ||
| + | '''for''' <tex>( A \in N )</tex> | ||
| + | <tex>\mathrm{FOLLOW}[A] = \varnothing </tex> | ||
| + | <tex>\mathrm{FOLLOW}[S] = \{\$\} </tex> <font color=green> // в стартовый терминал помещается символ конца строки </font> | ||
| + | changed = ''true'' | ||
| + | '''while''' changed | ||
| + | changed = ''false'' | ||
| + | '''for''' <tex>( A \to \alpha \in P )</tex> | ||
| + | '''for''' <tex>( B : \alpha = \beta B \gamma)</tex> | ||
| + | <tex> \mathrm{FOLLOW}[B]\ \cup =\ \mathrm{FIRST}(\gamma) \setminus \{\varepsilon\} </tex> | ||
| + | '''if''' <tex> \varepsilon \in \mathrm{FIRST}(\gamma) </tex> | ||
| + | <tex> \mathrm{FOLLOW}[B]\ \cup =\ \mathrm{FOLLOW}[A]</tex> | ||
| + | changed = ''true'' '''if''' <tex> \mathrm{FOLLOW}[B] </tex> изменился | ||
| + | </code> | ||
| + | Корректность данного алгоритма доказывается точно так же, как и корректность алгоритма конструирования <tex> \mathrm{FIRST} </tex>. | ||
== Пример == | == Пример == | ||
== См. также == | == См. также == | ||
Версия 22:23, 28 июня 2014
Для данной LL(1)-грамматики оказывается возможным построить нисходящий рекурсивный парсер, который по слову сможет построить его дерево разбора в грамматике или сказать, что слово не принадлежит языку грамматики. Более того, становится возможной даже автоматическая генерация парсеров для таких грамматик[1].
Чтобы написать парсер для LL(1)-грамматики, необходимо построить множества и , после чего по ним можно составить таблицу синтаксического анализатора.
Содержание
Построение FIRST
Для построения воспользуемся несколькими леммами, которые следуют прямо из определения. Пусть — цепочки из терминалов и нетерминалов, — символ из алфавита.
| Лемма (1): |
Данная лемма означает, что в множество правила , где — произвольный терминал или нетерминал, — нужно добавить , если для всех верно, что .
| Лемма (2): |
Псевдокод
Алгоритм строит для каждого терминала грамматики отображение в множество символов. Перед запуском алгоритма необходимо избавиться от бесполезных символов. Изначально каждое правило отображается в пустое множество.
function constructFIRST():
for
changed = true
while changed
changed = false
for
changed = true if изменился
| Утверждение: |
Приведённый алгоритм правильно строит множество для данной грамматики. |
|
Алгоритм на каждом шаге использует леммы, чтобы построить списки для каждого нетерминала. Поэтому он добавит только те терминалы, которые на самом деле лежат в .
Покажем, что алгоритм найдёт все символы из множества . Предположим, что в грамматике возможен вывод , и алгоритм не включил в . Докажем индукцией по числу шагов , что этого не может быть. Пусть за шагов алгоритм добавит символы в множество для каждого нетерминала , если . База индукции для числа шагов верна, если считать, что для всех терминалов нам известны . Если алгоритм корректно отрабатывает на -ом шаге, то он правильно отработает их на -ом шаге, потому что
Для алгоритм правильно построил по предположению индукции, а для он правильно построит по леммам, следовательно, переход доказан. К тому же алгоритм завершится за конечное число шагов, так как в для каждого нетерминала не может добавиться больше символов, чем есть в алфавите. |
Построение FOLLOW
Сформулируем похожие утверждения для построения .
| Лемма (3): |
Для каждого правила верно, что |
| Лемма (4): |
Для каждого правила вида или верно, что |
Псевдокод
Реализация построения получается сразу из лемм. Для алгоритма сначала требуется выполнить построение для грамматики.
function constructFOLLOW():
for
// в стартовый терминал помещается символ конца строки
changed = true
while changed
changed = false
for
for
if
changed = true if изменился
Корректность данного алгоритма доказывается точно так же, как и корректность алгоритма конструирования .