Предиктивный синтаксический анализ — различия между версиями

Материал из Викиконспекты
Перейти к: навигация, поиск
(Общая схема построения парсеров с помощью FIRST и FOLLOW)
Строка 1: Строка 1:
 
{{В разработке}}
 
{{В разработке}}
== Общая схема построения парсеров с помощью FIRST и FOLLOW ==
+
Для LL(1)-грамматик возможна автоматическая генерация парсеров, если известны множества FIRST и FOLLOW. Существуют общедоступные генераторы: [http://en.wikipedia.org/wiki/ANTLR ANTLR], [http://en.wikipedia.org/wiki/GNU_bison GNU bison], [http://en.wikipedia.org/wiki/Yacc Yacc].
Для LL(1) грамматик возможна автоматическая генерация парсеров, если известны множества FIRST и FOLLOW. Существуют общедоступные генераторы: [http://en.wikipedia.org/wiki/ANTLR ANTLR], [http://en.wikipedia.org/wiki/GNU_bison GNU bison], [http://en.wikipedia.org/wiki/Yacc Yacc].
+
 
 +
== Общая схема построения парсеров с помощью <tex>FIRST</tex> и <tex>FOLLOW</tex> ==
  
 
Пусть <tex>\Gamma</tex> {{---}} LL(1)-грамматика. Построим для нее парсер.
 
Пусть <tex>\Gamma</tex> {{---}} LL(1)-грамматика. Построим для нее парсер.
  
Для каждого нетерминала A : <tex>A \rightarrow \alpha_1 \mid \alpha_2 \mid ... \mid \alpha_k </tex> создадим функцию A() : Node, возвращающую фрагмент дерева разбора, выведенный из нетерминала A.
+
Для каждого нетерминала <tex>A</tex> : <tex>A \rightarrow \alpha_1 \mid \alpha_2 \mid ... \mid \alpha_k </tex> создадим функцию A() : Node, возвращающую фрагмент дерева разбора, выведенный из нетерминала <tex>A</tex>.
  
 
Здесь Node {{---}} структура вида:
 
Здесь Node {{---}} структура вида:
Строка 17: Строка 18:
 
Токен {{---}} один или несколько нетерминалов, для удобства объединяемые по смыслу в одну логическую единицу.
 
Токен {{---}} один или несколько нетерминалов, для удобства объединяемые по смыслу в одну логическую единицу.
 
curToken {{---}} текущий токен строки.
 
curToken {{---}} текущий токен строки.
nextToken {{---}} следующий за ним токен.
+
nextToken() {{---}} записывает в curToken следующий за ним токен.
  
 
  A() : Node
 
  A() : Node
Строка 23: Строка 24:
 
     switch (curToken) :
 
     switch (curToken) :
 
           case : <tex>FIRST(\alpha_1) \cup ((\varepsilon \in FIRST(\alpha_1))  ?  FOLLOW(A)  :  \varnothing)</tex> :
 
           case : <tex>FIRST(\alpha_1) \cup ((\varepsilon \in FIRST(\alpha_1))  ?  FOLLOW(A)  :  \varnothing)</tex> :
             // \alpha_1 = x_1x_2..x_{t_1}
+
             // <tex>\alpha_1 = x_1x_2..x_{t_1}</tex>
             for x_1 .. x_{t_1}
+
             for <tex>x_1 .. x_{t_1}</tex>
                 if x_1 is terminal
+
                 if <tex>x_1</tex> is terminal
                     consume(x_1)
+
                     consume(<tex>x_1</tex>)
                     res.addChild(new Node("x_1")
+
                     res.addChild(new Node("<tex>x_1</tex>")
 
                     nextToken()
 
                     nextToken()
 
                 else
 
                 else
                     Node t = X_1()
+
                     Node t = <tex>X_1()</tex>
 
                     res.addChild(t)
 
                     res.addChild(t)
 
             break
 
             break
Строка 49: Строка 50:
  
 
== Пример ==
 
== Пример ==
Рассмотрим построение парсера на примере грамматики арифметических выражений.
+
Рассмотрим построение парсера на примере LL(1)-грамматики арифметических выражений.
Запишем грамматику.
 
 
 
<tex>
 
E \to T + E \mid T \\
 
T \to F \times T \mid F \\
 
F \to n \mid (E)
 
</tex>
 
 
 
Данная грамматика не является LL(1)-грамматикой, так как содержит правое ветвление, от него нужно избавиться перед построением парсера:
 
  
 
<tex>  
 
<tex>  
Строка 68: Строка 60:
 
</tex>
 
</tex>
  
Теперь грамматика стала LL(1)-грамматикой, построим для нее множества FIRST и FOLLOW (их построение подробно разобрано [[Построение FIRST и FOLLOW#Пример | здесь]]).
+
Построим для нее множества FIRST и FOLLOW (их построение подробно разобрано [[Построение FIRST и FOLLOW#Пример | здесь]]).
  
 
{| style="background-color:#CCC;margin:0.5px"
 
{| style="background-color:#CCC;margin:0.5px"
Строка 141: Строка 133:
 
     return res
 
     return res
  
Функции для T и T' строятся аналогично.
+
Функции для <tex>T</tex> и <tex>T'</tex> строятся аналогично.
  
 
{{TODO | t = Картинки примеров разбора чего-нибудь типа 1+2*3}}
 
{{TODO | t = Картинки примеров разбора чего-нибудь типа 1+2*3}}
 
{{TODO | t = Построение таблицы предиктивного анализа}}
 
{{TODO | t = Построение таблицы предиктивного анализа}}

Версия 17:06, 24 мая 2015

Эта статья находится в разработке!

Для LL(1)-грамматик возможна автоматическая генерация парсеров, если известны множества FIRST и FOLLOW. Существуют общедоступные генераторы: ANTLR, GNU bison, Yacc.

Общая схема построения парсеров с помощью [math]FIRST[/math] и [math]FOLLOW[/math]

Пусть [math]\Gamma[/math] — LL(1)-грамматика. Построим для нее парсер.

Для каждого нетерминала [math]A[/math] : [math]A \rightarrow \alpha_1 \mid \alpha_2 \mid ... \mid \alpha_k [/math] создадим функцию A() : Node, возвращающую фрагмент дерева разбора, выведенный из нетерминала [math]A[/math].

Здесь Node — структура вида:

Node
    children : list<Node>
    value : string // имя нетерминала или текст терминала
    addChild(Node) // функция, подвешивающая поддерево к данному узлу

Тут картинка про строку.

Токен — один или несколько нетерминалов, для удобства объединяемые по смыслу в одну логическую единицу. curToken — текущий токен строки. nextToken() — записывает в curToken следующий за ним токен.

A() : Node
    res = Node("A")
    switch (curToken) :
         case : [math]FIRST(\alpha_1) \cup ((\varepsilon \in FIRST(\alpha_1))  ?  FOLLOW(A)  :  \varnothing)[/math] :
            // [math]\alpha_1 = x_1x_2..x_{t_1}[/math]
            for [math]x_1 .. x_{t_1}[/math]
                if [math]x_1[/math] is terminal
                    consume([math]x_1[/math])
                    res.addChild(new Node("[math]x_1[/math]")
                    nextToken()
                else
                    Node t = [math]X_1()[/math]
                    res.addChild(t)
            break
        case [math]FIRST(\alpha_2) \cup ((\varepsilon \in FIRST(\alpha_2))  ?  FOLLOW(A)  :  \varnothing)[/math] : 
            ...
            break
        ...
        default :
            error("unexpected char")
    return res
consume(char c) 
    if (curToken != c)
        error("expected" + c)
    nextToken()

Такой парсер не только разбирает строку, но и находит ошибки в неудовлетворяющих грамматике выражениях.

Пример

Рассмотрим построение парсера на примере LL(1)-грамматики арифметических выражений.

[math] E \to TE' \\ E' \to +TE' \mid \varepsilon \\ T \to FT' \\ T' \to \times FT' \mid \varepsilon \\ F \to n \mid (E) [/math]

Построим для нее множества FIRST и FOLLOW (их построение подробно разобрано здесь).

Правило FIRST FOLLOW
[math]E[/math] [math]\{\ n,\ (\ \} [/math] [math]\{\ \$,\ )\ \} [/math]
[math]E'[/math] [math]\{\ +,\ \varepsilon\ \} [/math] [math]\{\ \$,\ )\ \} [/math]
[math]T[/math] [math]\{\ n,\ (\ \} [/math] [math]\{\ +,\ \$\ ,\ )\ \}[/math]
[math]T'[/math] [math]\{\ \times,\ \varepsilon\ \} [/math] [math]\{\ +,\ \$\ ,\ )\ \}[/math]
[math]F[/math] [math]\{\ n,\ (\ \} [/math] [math]\{\ \times, \ +,\ \$\ ,\ )\ \} [/math]

Построим функции обработки некоторых нетерминалов.

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

Функции для [math]T[/math] и [math]T'[/math] строятся аналогично.


TODO: Картинки примеров разбора чего-нибудь типа 1+2*3

TODO: Построение таблицы предиктивного анализа