Предиктивный синтаксический анализ — различия между версиями
(→Общая схема построения парсеров с помощью FIRST и FOLLOW) |
|||
| Строка 5: | Строка 5: | ||
Пусть <tex>\Gamma</tex> {{---}} LL(1)-грамматика. Построим для нее парсер. | Пусть <tex>\Gamma</tex> {{---}} LL(1)-грамматика. Построим для нее парсер. | ||
| − | Для каждого нетерминала A : <tex>A \rightarrow \alpha_1 | + | Для каждого нетерминала A : <tex>A \rightarrow \alpha_1 \mid \alpha_2 \mid ... \mid \alpha_k </tex> создадим функцию A() : Node, возвращающую фрагмент дерева разбора, выведенный из нетерминала A. |
Здесь Node {{---}} структура вида: | Здесь Node {{---}} структура вида: | ||
| Строка 40: | Строка 40: | ||
return res | return res | ||
| − | {{ | + | == Пример == |
| + | Рассмотрим построение парсера на примере грамматики арифметических выражений. | ||
| + | Запишем грамматику. | ||
| + | |||
| + | <tex> | ||
| + | E \to T + E \mid T \\ | ||
| + | T \to F \times T \mid F \\ | ||
| + | F \to n \mid (E) | ||
| + | </tex> | ||
| + | |||
| + | Данная грамматика не является LL(1)-грамматикой, так как содержит правое ветвление, от него нужно избавиться перед построением парсера: | ||
| + | |||
| + | <tex> | ||
| + | E \to TE' \\ | ||
| + | E' \to +TE' \mid \varepsilon \\ | ||
| + | T \to FT' \\ | ||
| + | T' \to \times FT' \mid \varepsilon \\ | ||
| + | F \to n \mid (E) | ||
| + | </tex> | ||
| + | |||
| + | Теперь грамматика стала LL(1)-грамматикой, построим для нее множества FIRST и FOLLOW (их построение подробно разобрано [[Построение FIRST и FOLLOW#Пример | здесь]]). | ||
| + | |||
| + | {| style="background-color:#CCC;margin:0.5px" | ||
| + | !style="background-color:#EEE"| Правило | ||
| + | !style="background-color:#EEE"| FIRST | ||
| + | !style="background-color:#EEE"| FOLLOW | ||
| + | |- | ||
| + | |style="background-color:#FFF;padding:2px 30px"| <tex>E</tex> | ||
| + | |style="background-color:#FFF;padding:2px 30px"| <tex>\{\ n,\ (\ \} </tex> | ||
| + | |style="background-color:#FFF;padding:2px 30px"| <tex>\{\ \$,\ )\ \} </tex> | ||
| + | |- | ||
| + | |style="background-color:#FFF;padding:2px 30px"| <tex>E'</tex> | ||
| + | |style="background-color:#FFF;padding:2px 30px"| <tex>\{\ +,\ \varepsilon\ \} </tex> | ||
| + | |style="background-color:#FFF;padding:2px 30px"| <tex>\{\ \$,\ )\ \} </tex> | ||
| + | |- | ||
| + | |style="background-color:#FFF;padding:2px 30px"| <tex>T</tex> | ||
| + | |style="background-color:#FFF;padding:2px 30px"| <tex>\{\ n,\ (\ \} </tex> | ||
| + | |style="background-color:#FFF;padding:2px 30px"| <tex>\{\ +,\ \$\ ,\ )\ \}</tex> | ||
| + | |- | ||
| + | |style="background-color:#FFF;padding:2px 30px"| <tex>T'</tex> | ||
| + | |style="background-color:#FFF;padding:2px 30px"| <tex>\{\ \times,\ \varepsilon\ \} </tex> | ||
| + | |style="background-color:#FFF;padding:2px 30px"| <tex>\{\ +,\ \$\ ,\ )\ \}</tex> | ||
| + | |- | ||
| + | |style="background-color:#FFF;padding:2px 30px"| <tex>F</tex> | ||
| + | |style="background-color:#FFF;padding:2px 30px"| <tex>\{\ n,\ (\ \} </tex> | ||
| + | |style="background-color:#FFF;padding:2px 30px"| <tex>\{\ \times, \ +,\ \$\ ,\ )\ \} </tex> | ||
| + | |} | ||
| + | |||
| + | |||
| + | |||
{{TODO | t = Картинки примеров разбора чего-нибудь типа 1+2*3}} | {{TODO | t = Картинки примеров разбора чего-нибудь типа 1+2*3}} | ||
{{TODO | t = Построение таблицы предиктивного анализа}} | {{TODO | t = Построение таблицы предиктивного анализа}} | ||
Версия 15:28, 24 мая 2015
Общая схема построения парсеров с помощью FIRST и FOLLOW
Для LL(1) грамматик возможна автоматическая генерация парсеров, если известны множества FIRST и FOLLOW. Существуют общедоступные генераторы: ANTLR, GNU bison, Yacc.
Пусть — LL(1)-грамматика. Построим для нее парсер.
Для каждого нетерминала A : создадим функцию A() : Node, возвращающую фрагмент дерева разбора, выведенный из нетерминала A.
Здесь Node — структура вида:
Node
children : list<Node>
value : string
Тут картинка про строку.
A() : Node
res = Node("A")
switch (curToken) :
case : FIRST(\alpha_1) :
// \alpha_1 = X_1x_2..x_{t_1}
// X_1 — нетерминал
Node t = X_1()
res.addChild(t)
// x_2 — терминал
if (curToken != x_2)
error("expected x_2")
res.addChild(new Node("x_2")
nextToken()
// x_3
...
break;
case FIRST(\alpha_2) :
// \varepsilon \in FIRST(\alpha_2)
case FOLLOW(A) :
...
break;
...
default :
error("unexpected char")
return res
Пример
Рассмотрим построение парсера на примере грамматики арифметических выражений. Запишем грамматику.
Данная грамматика не является LL(1)-грамматикой, так как содержит правое ветвление, от него нужно избавиться перед построением парсера:
Теперь грамматика стала LL(1)-грамматикой, построим для нее множества FIRST и FOLLOW (их построение подробно разобрано здесь).
| Правило | FIRST | FOLLOW |
|---|---|---|
TODO: Картинки примеров разбора чего-нибудь типа 1+2*3
TODO: Построение таблицы предиктивного анализа