LR(1)-разбор — различия между версиями
Xottab (обсуждение | вклад) м |
Xottab (обсуждение | вклад) (→Псевдокод) |
||
Строка 94: | Строка 94: | ||
====Псевдокод==== | ====Псевдокод==== | ||
<wikitex> | <wikitex> | ||
− | Псевдокод построения множеств | + | Псевдокод построения множеств $CLOSURE$ и $GOTO$, а также множества пунктов $items$: |
<code> | <code> | ||
Set<Item> CLOSURE(Set<Item> I): | Set<Item> CLOSURE(Set<Item> I): | ||
Строка 130: | Строка 130: | ||
'''return''' C; | '''return''' C; | ||
</code> | </code> | ||
+ | </wikitex> | ||
+ | ====Пример==== | ||
+ | <wikitex> | ||
+ | Рассмотрим следующую грамматику $G'$: | ||
+ | * $S'\rightarrow S$ | ||
+ | * $S\rightarrow CC$ | ||
+ | * $S\rightarrow cC|d$ | ||
+ | Запустим процедуру $items(G')$. Она начинается с вычисления $CLOSURE([S\rightarrow S', \char36])$. Это правило вида $[A\rightarrow\alpha\cdot B\beta, a]$, где $A=S';\alpha=\epsilon;B=S;\beta=\epsilon;a=\char36$. Т.к. в таком случае $FIRST(\beta\alpha) = {\char36}$, то мы добавим только правило $[S\rightarrow\cdot CC,\char36]$. | ||
+ | |||
+ | Продолжив вычислять замыкание таким образом, мы добавим во множество пунктов $[C\rightarrow\cdot C, c]$, $C\rightarrow\cdot C, d]$, $C\rightarrow\cdot d, c]$, и $C\rightarrow\cdot d, d]$. Т.к. ни один из новых пунктов не имеет вид $[A\rightarrow\alpha\cdot B\beta, a]$(справа от точки во всех пунктах терминалы), то функция $CLOSURE$ завершает свою работу и начальное множество пунктов в данном случае равно: | ||
+ | $$I_0: \{[S\rightarrow S', \char36],[S\rightarrow\cdot CC,\char36],[C\rightarrow\cdot C, c/d],[C\rightarrow\cdot d, c/d]\}$$ | ||
+ | Следующим шагом процедуры $items$ будет вычисление функции переходов автомата $GOTO(I_0,X)$ для всех терминалов $I_0$. | ||
+ | |||
</wikitex> | </wikitex> |
Версия 12:10, 27 июня 2015
<wikitex> В некоторых случаях SLR-разбор может дать неправильный результат разбора. В таких случаях используют более сложные методы, такие как $LR(1)$ и $LALR$ - разбор. Рассмотрим первый из них. </wikitex>
Содержание
Отличия от SLR-разбора
<wikitex> Основным отличием $LR(1)$ - разбора от SLR-разбора является использование предпросмотра (англ. lookahead) символов.
Приведём пример, ситуации, в которой SLR-разбор не справится с задачей:
Рассмотрим грамматику вида: $ S \to L=R | R \\ L \to *R | id \\ R \to L $
Покажем её канонический LR(0) - набор:
$I_0$ | $I_1$ | $I_2$ | $I_3$ | $I_4$ | $I_5$ | $I_6$ | $I_7$ | $I_8$ | $I_9$ |
---|---|---|---|---|---|---|---|---|---|
$S' \to \cdot S \\ S \to \cdot L = R \\ S \to \cdot R \\ L \to \cdot * R \\ L \to \cdot id \\ R \to \cdot L$ |
$S' \to S \cdot$ |
$S \to L \cdot = R \\ R \to L \cdot$ |
$S \to R \cdot$ |
$L \to * \cdot R \\ R \to \cdot L \\ L \to \cdot * R \\ L \to \cdot id$ |
$L \to id \cdot$ |
$S \to L = \cdot R \\ R \to \cdot L \\ L \to \cdot * R \\ L \to \cdot id$ |
$L \to * R \cdot$ |
$R \to L \cdot$ |
$S \to L = R \cdot$ |
Рассмотрим пункт $I_2$. Если SLR-парсер находится в состоянии $I_2$ и очередной входной символ равен $=$, то парсер выполняет свёртку в соответствии с продукцией $R \to L$, что неверно, т.к. в этой грамматике не выводится выражение $R=...$ и парсер должен был выполнить перенос, а не свёртку.
Чтобы решить эту проблему, необходимо хранить в пункте больший объём информации, который позволит не делать таких ошибочных свёрток </wikitex>
Канонические LR(1)-пункты
<wikitex> Основная идея заключается в том, чтобы хранить в пунктах больше информации, чтобы не производить некорректных свёрток. Добавим в пункт второй компонент: терминальный символ. Таким образом, $LR(1)$ -пункты будут выглядеть следующим образом:
$[A\rightarrow\alpha\cdot\beta, a]$, где первая часть - продукция, а вторая - терминал или маркер конца входной строки $\char36$. Здесь $a$ называется предпросмотром (англ. lookahead) пункта, а цифра $1$ в $LR(1)$ означает его длину. Теперь мы будем выполнять свёртку в соответствии с продукцией $A\rightarrow\alpha$, только в том случае, если пункт $[A\rightarrow\alpha\cdot\beta, a]$ принадлежит состоянию на вершине стека, и $a$ - входной символ.
Определение: |
Назовём $LR(1)$ - пункт $[A\rightarrow\alpha\cdot\beta, a]$ допустимым (англ. valid) для активного префикса $\gamma$, если существует правое порождение $S\Rightarrow^{*}\delta A w\Rightarrow\delta\alpha\beta w$, где верно одно из трёх:
|
</wikitex>
Построение множеств LR(1)-пунктов
<wikitex> Метод построения похож на метод для $LR(0)$ - разбора, с двумя изменёнными функциями: $CLOSURE(I)$ - замыкание множества пунктов, и $GOTO(X,I)$ - функция переходов в автомате по символу $X$.
Лемма: |
$$\forall{b} |
Доказательство: |
Рассмотрим пункт вида $[A\rightarrow\alpha\cdot B\beta, a]$ в множестве пунктов, допустимых для некоторого активного префикса $\gamma$. Тогда существует правое порождение $S\Rightarrow^{*}\delta Aax\Rightarrow\delta\alpha B\beta ax$, где $\gamma=\delta\alpha$. Предположим, что $\beta ax$ порождает строку терминалов $by$. Тогда для каждой продукции вида $\forall{B\rightarrow\eta}\exists{\eta}$ мы имеем порождение $ S\Rightarrow^{*}\delta Bby\Rightarrow\delta\eta by$. Таким образом, $[B\rightarrow\cdot\eta,b]$ является допустимым для $\gamma$. Заметим, что $b$ может быть первым терминалом, порожденным из $\beta$, либо, возможно что $\beta$ порождает $\epsilon$ слева: $\beta ax\Rightarrow^{*}by$, следовательно $b=a$. Таким образом, $b\in FIRST(\beta ax)$. Поскольку $x$ не может содержать первый терминал из $by$, то $FIRST(\beta ax)=FIRST(\beta a)$ Значит, $b\in FIRST(\beta a)$. |
</wikitex>
Псевдокод
<wikitex>
Псевдокод построения множеств $CLOSURE$ и $GOTO$, а также множества пунктов $items$:
Set<Item> CLOSURE(Set<Item> I): bool changed; Set<Item> $J$=$I$; repeat changed = false; for $[A\rightarrow\alpha\cdot B\beta, a]\in I$ for $(B\rightarrow\gamma)\in G'$ for $b\in FIRST(\beta\alpha)$ J.add($[B\rightarrow\cdot\gamma,b]$); changed = true until !changed; return J;
Set<Item> GOTO(Set<Item> I, X): Set<Item> $J$=$\varnothing$; for $[A\rightarrow\alpha\cdot X\beta, a]\in I$ J.add($[A\rightarrow\alpha X\cdot\beta, a]$); return $CLOSURE(J)$;
Set<Set<Item>> items($G'$): bool changed; Set<Set<Item>> $C$ = $CLOSURE({S'\rightarrow\cdot S,\char36})$; repeat changed = false; for Set<Item> $I\subset C$ for $X \in terminals(G')$ if $GOTO(I,X)\neq\varnothing$ and $GOTO(I,X)\not\subset C$ C.add($GOTO(I,X)$); changed = true until !changed; return C;
</wikitex>
Пример
<wikitex> Рассмотрим следующую грамматику $G'$:
- $S'\rightarrow S$
- $S\rightarrow CC$
- $S\rightarrow cC|d$
Запустим процедуру $items(G')$. Она начинается с вычисления $CLOSURE([S\rightarrow S', \char36])$. Это правило вида $[A\rightarrow\alpha\cdot B\beta, a]$, где $A=S';\alpha=\epsilon;B=S;\beta=\epsilon;a=\char36$. Т.к. в таком случае $FIRST(\beta\alpha) = {\char36}$, то мы добавим только правило $[S\rightarrow\cdot CC,\char36]$.
Продолжив вычислять замыкание таким образом, мы добавим во множество пунктов $[C\rightarrow\cdot C, c]$, $C\rightarrow\cdot C, d]$, $C\rightarrow\cdot d, c]$, и $C\rightarrow\cdot d, d]$. Т.к. ни один из новых пунктов не имеет вид $[A\rightarrow\alpha\cdot B\beta, a]$(справа от точки во всех пунктах терминалы), то функция $CLOSURE$ завершает свою работу и начальное множество пунктов в данном случае равно: $$I_0: \{[S\rightarrow S', \char36],[S\rightarrow\cdot CC,\char36],[C\rightarrow\cdot C, c/d],[C\rightarrow\cdot d, c/d]\}$$ Следующим шагом процедуры $items$ будет вычисление функции переходов автомата $GOTO(I_0,X)$ для всех терминалов $I_0$.
</wikitex>