Предиктивный синтаксический анализ — различия между версиями
Строка 17: | Строка 17: | ||
res = Node("A") | res = Node("A") | ||
switch (curToken) : | switch (curToken) : | ||
− | case : FIRST(\alpha_1) : | + | for \alpha_1, \alpha_2 .. \alpha_k |
− | + | case : <tex>FIRST(\alpha_1) \cup ((\varepsilon \in FIRST(\alpha_1)) ? FOLLOW(A) : \varnothing)</tex> : | |
− | + | // \alpha_1 = X_1x_2..x_{t_1} | |
− | + | // X_1 {{---}} нетерминал | |
− | + | Node t = X_1() | |
− | + | res.addChild(t) | |
− | + | // x_2 {{---}} терминал | |
− | + | consume(x_2) | |
− | + | res.addChild(new Node("x_2") | |
− | + | nextToken() | |
− | + | // x_3 | |
− | + | ... | |
− | + | break; | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
default : | default : | ||
error("unexpected char") | error("unexpected char") | ||
return res | return res | ||
+ | |||
+ | consume(char c) | ||
+ | if (curToken != c) | ||
+ | error("expected" + c) | ||
+ | nextToken() | ||
== Пример == | == Пример == | ||
Строка 100: | Строка 99: | ||
error("unexpected char") | error("unexpected char") | ||
return res | return res | ||
− | |||
− | |||
− | |||
− | |||
− | |||
E'() | E'() |
Версия 16:12, 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) :
for \alpha_1, \alpha_2 .. \alpha_k
case :
:
// \alpha_1 = X_1x_2..x_{t_1}
// X_1 — нетерминал
Node t = X_1()
res.addChild(t)
// x_2 — терминал
consume(x_2)
res.addChild(new Node("x_2")
nextToken()
// x_3
...
break;
default :
error("unexpected char")
return res
consume(char c) if (curToken != c) error("expected" + c) nextToken()
Пример
Рассмотрим построение парсера на примере грамматики арифметических выражений. Запишем грамматику.
Данная грамматика не является 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
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: Построение таблицы предиктивного анализа