Построение FIRST и FOLLOW

Материал из Викиконспекты
Версия от 22:06, 28 июня 2014; Shersh (обсуждение | вклад) (Построение FOLLOW)
Перейти к: навигация, поиск
Эта статья находится в разработке!

Для данной LL(1)-грамматики оказывается возможным построить нисходящий рекурсивный парсер, который по слову сможет построить его дерево разбора в грамматике или сказать, что слово не принадлежит языку грамматики. Более того, становится возможной даже автоматическая генерация парсеров для таких грамматик[1].

Чтобы написать парсер для LL(1)-грамматики, необходимо построить множества [math] \mathrm{FIRST} [/math] и [math] \mathrm{FOLLOW} [/math], после чего по ним можно составить таблицу синтаксического анализатора.

Построение FIRST

Для построения [math] \mathrm{FIRST} [/math] воспользуемся несколькими леммами, которые следуют прямо из определения. Пусть [math] \alpha, \beta [/math] — цепочки из терминалов и нетерминалов, [math] c [/math] — символ из алфавита.

Лемма (1):
[math] \mathrm{FIRST}(\alpha \beta) = \mathrm{FIRST}(\alpha) \cup (\mathrm{FIRST}(\beta)\ \mathrm{if}\ \varepsilon \in \mathrm{FIRST}(\alpha)) [/math]

Данная лемма означает, что в множество [math] \mathrm{FIRST} [/math] правила [math] A \to X_1 X_2 \dots X_k [/math], где [math] X_i,\ (1 \leqslant i \leqslant k) [/math] — произвольный терминал или нетерминал, — нужно добавить [math] \mathrm{FIRST}(X_i) [/math], если для всех [math] 1 \leqslant j \lt i [/math] верно, что [math] X_j \Rightarrow^* \varepsilon [/math].

Лемма (2):
[math] \mathrm{FIRST}(c \alpha) = \{c\}[/math]
[math] \mathrm{FIRST}(\varepsilon) = \{\varepsilon \}[/math]

Псевдокод

Алгоритм строит для каждого терминала грамматики [math]\Gamma = \langle \Sigma, N, S, P \rangle[/math] отображение в множество символов. Перед запуском алгоритма необходимо избавиться от бесполезных символов. Изначально каждое правило отображается в пустое множество.

 function constructFIRST():
     for [math]( A  \in N )[/math]
         [math]\mathrm{FIRST}[A] =  \varnothing [/math]
     changed = true
     while changed
         changed = true
         for [math]( A \to \alpha \in P )[/math]
             [math] \mathrm{FIRST}[A]\ \cup =\ \mathrm{FIRST}(\alpha) [/math]
             changed = true if [math] \mathrm{FIRST}[A] [/math] изменился

Утверждение:
Приведённый алгоритм правильно строит множество [math] \mathrm{FIRST} [/math] для данной грамматики.
[math]\triangleright[/math]

[math] \Leftarrow [/math]

Алгоритм на каждом шаге использует леммы, чтобы построить списки [math] \mathrm{FIRST} [/math] для каждого нетерминала. Поэтому он добавит только те терминалы, которые на самом деле лежат в [math] \mathrm{FIRST} [/math].

[math] \Rightarrow [/math]

Покажем, что алгоритм найдёт все символы из множества [math] \mathrm{FIRST} [/math].

Предположим, что в грамматике возможен вывод [math] A \Rightarrow^* c \gamma [/math], и алгоритм не включил [math] c [/math] в [math] \mathrm{FIRST}[A] [/math]. Докажем индукцией по числу шагов [math] \mathrm{while} [/math], что этого не может быть.

Пусть за [math] k [/math] шагов алгоритм добавит символы [math] c [/math] в множество [math] \mathrm{FIRST} [/math] для каждого нетерминала [math] A [/math], если [math] A \Rightarrow^k c \gamma [/math]. База индукции для числа шагов [math] 0 [/math] верна, если считать, что для всех терминалов нам известны [math] \mathrm{FIRST} [/math]. Если алгоритм корректно отрабатывает на [math](k-1)[/math]-ом шаге, то он правильно отработает их на [math]k[/math]-ом шаге, потому что

[math] A \Rightarrow \beta \Rightarrow^{k-1} c \gamma [/math]

Для [math] \beta [/math] алгоритм правильно построил [math] \mathrm{FIRST} [/math] по предположению индукции, а для [math] A [/math] он правильно построит по леммам, следовательно, переход доказан.

К тому же алгоритм завершится за конечное число шагов, так как в [math] \mathrm{FIRST} [/math] для каждого нетерминала не может добавиться больше символов, чем есть в алфавите.
[math]\triangleleft[/math]

Построение FOLLOW

Сформулируем похожие утверждения для построения [math] \mathrm{FOLLOW} [/math].

Лемма (1):
Для каждого правила [math] A \to \alpha B \gamma [/math] верно, что [math]\mathrm{FIRST}(\gamma) \subset \mathrm{FOLLOW}(B) [/math]

Пример

См. также

Примечания

Источники информации