Предиктивный синтаксический анализ — различия между версиями
(→Пример) |
|||
| Строка 8: | Строка 8: | ||
Здесь Node {{---}} структура вида: | Здесь 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 | |
== Пример == | == Пример == | ||
| Строка 90: | Строка 90: | ||
Построим функции обработки некоторых нетерминалов. | Построим функции обработки некоторых нетерминалов. | ||
| − | + | E() | |
| − | + | res = Node("E") | |
| − | + | switch(curToken) | |
| − | + | case 'n', '(' : | |
| − | + | res.addChild(T()) | |
| − | + | res.addChild(E'()) | |
| − | + | break | |
| − | + | default : | |
| − | + | error("unexpected char") | |
| − | + | return res | |
| − | + | consume(char c) | |
| − | + | if (curToken != c) | |
| − | + | error("expected" + c) | |
| − | + | nextToken() | |
| − | + | E'() | |
| − | + | res = Node("E'") | |
| − | + | switch(curToken) | |
| − | + | case '+' : | |
| − | + | consume('+') | |
| − | + | res.addChild(Node("+")) | |
| − | + | res.addChild(T()) | |
| − | + | res.addChild(E'()) | |
| − | + | break | |
| − | + | case '$', ')' : | |
| − | + | break | |
| − | + | default : | |
| − | + | error("unexpected char") | |
| − | + | return res | |
| − | |||
| − | + | F() | |
| − | + | res = Node("F") | |
| − | + | switch(curToken) | |
| − | + | case 'n' : | |
| − | + | consume('n') | |
| − | + | res.addChild(Node("n")) | |
| − | + | break | |
| − | + | case '(' : | |
| − | + | consume('(') | |
| − | + | res.addChild(Node("(")) | |
| − | + | res.addChild(E()) | |
| − | + | consume(')') | |
| − | + | res.addChild(Node(")")) | |
| − | + | default : | |
| − | + | error("unexpected char") | |
| − | + | return res | |
| − | |||
| − | |||
Функции для T и T' строятся аналогично. | Функции для T и T' строятся аналогично. | ||
Версия 15:54, 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 |
|---|---|---|
Построим функции обработки некоторых нетерминалов.
E()
res = Node("E")
switch(curToken)
case 'n', '(' :
res.addChild(T())
res.addChild(E'())
break
default :
error("unexpected char")
return res
consume(char c)
if (curToken != c)
error("expected" + c)
nextToken()
E'()
res = Node("E'")
switch(curToken)
case '+' :
consume('+')
res.addChild(Node("+"))
res.addChild(T())
res.addChild(E'())
break
case '$', ')' :
break
default :
error("unexpected char")
return res
F()
res = Node("F")
switch(curToken)
case 'n' :
consume('n')
res.addChild(Node("n"))
break
case '(' :
consume('(')
res.addChild(Node("("))
res.addChild(E())
consume(')')
res.addChild(Node(")"))
default :
error("unexpected char")
return res
Функции для T и T' строятся аналогично.
TODO: Картинки примеров разбора чего-нибудь типа 1+2*3
TODO: Построение таблицы предиктивного анализа