Изменения

Перейти к: навигация, поиск

Атрибутные транслирующие грамматики

2592 байта добавлено, 17:00, 11 февраля 2019
Атрибуты в ANTLR
S \to E \\
E \to E + T \mid T \\
T \to T \times * F \mid F \\
F \to n \mid (E)
$
|style="background-color:#FFF;padding:2px 30px"| $E_0 \to E_1 + T\ \{ADD\ res = op_1 + op_2\}$
|style="background-color:#FFF;padding:2px 30px"| $ADD.op_1=E_1.val \\ ADD.op_2=T.val \\ E_0.val=ADD.res $
|style="background-color:#FFF;padding:2px 30px"| В фигурных скобках {{---}} действия транслирующего символа ADD. $op_1$, $op_2$ и $res$ {{- --}} атрибуты транслирующего символа.
|-
|style="background-color:#FFF;padding:2px 30px"| $E \to T$
|style="background-color:#FFF;padding:2px 30px"|
|-
|style="background-color:#FFF;padding:2px 30px"| $T_0 \to T_1 \times * F \ \{MUL\ res = op_1 \times op_2\}$
|style="background-color:#FFF;padding:2px 30px"| $MUL.op_1=T.val \\ MUL.op_2=F.val \\ T_0.val=MUL.res$
|style="background-color:#FFF;padding:2px 30px"| В фигурных скобках {{---}} действия транслирующего символа MUL. $op_1$, $op_2$ и $res$ {{- --}} атрибуты транслирующего символа.
|-
|style="background-color:#FFF;padding:2px 30px"| $T \to F$
Выпишем продукции (с транслирующими символами) и ассоциируем с ними семантические правила
(здесь $\{ENTRY {{...}} \}$ {{- --}} [[Атрибутные_транслирующие_грамматики#tr_char|транслирующий символ]]. Если в продукции несколько раз встречается одинаковый нетерминал, будем добавлять к нему индексы, считая от начала продукции.):
{| style="background-color:#CCC;margin:0.5px"
|-
|style="background-color:#FFF;padding:2px 30px"| $L_0 \to L_1,id\ \{ENTRY addtype(key, value)\}$
|style="background-color:#FFF;padding:2px 30px"| $L_1.ininh =LL0.inh \\ ENTRY.key=id.text \\ ENTRY.value=LL_0.inh$
|-
|style="background-color:#FFF;padding:2px 30px"| $L \to id\ \{ENTRY addtype(key, value)\}$
|}
Семантическое правило $L.inh = T.type$, связанное с продукцией $D \to TL$, определяет наследуемый атрибут $L.inh$ как тип объявления. Затем приведенные правила распространяют этот тип вниз по дереву разбора с использованием атрибута $L.inh$. Транслирующий символ $ENTRY$, связанный с продукциями для $L$, вызывает процедуру $addtype$ для добавления типа каждого идентификатора к его записи в таблице символов (по ключу, определяемому атрибутом $text$).
[[Файл:Real_id1,_id2,_id3.png|600px|center|thumb|аннотированное Аннотированное дерево разбора для '''$\mathbf{real}\ id1,\ id2,\ id3$'''|600px]]
</wikitex>
==Пример работы с атрибутами в нисходящем разборе==
<wikitex>
Рассмотрим работы с атрибутами на примере $LL(1)$-грамматики арифметических выражений, которая уже была разобрана [[Построение FIRST и FOLLOW#Пример | ранее]] и расширим код [[Предиктивный_синтаксический_анализ | разборщика]] для нее:
$
$
В данной реализации рекурсивные функции от нетерминалов получают на вход(если необходимо) наследуемые атрибуты узла и возвращают вершины дерева разбора, в атрибутах которых записан результат вычислений соответствующего подвыражения. Однако этот код легко изменить, чтобы он только вычислял значение выражения и не строил дерево разбора. Как мы видим, $val$ {{--- }} синтезируемый атрибут, $acc$ {{--- }} наследуемый атрибут, $ADD$ {{-- -}} транслирующий символ. Синим подсвечены строки, отвечающие за работы работу с атрибутами. Здесь <tex>\mathtt{Node}</tex> {{---}} структура следующего вида: '''struct''' Node children : '''map<String, Node>''' name : '''string''' val : '''int''' <font color="green">// атрибут нетерминала</font> '''function''' addChild('''Node''') <font color="green">// функция, подвешивающая поддерево к данному узлу</font> 
E() : '''Node'''
'''switch''' (curToken)
'''case''' n, '(' :
res.addChild(T()) <font color="green">// подвешиваем левого сына</font> <font color="blue">temp = res.children["T"].val</font> <font color="green">// атрибут левого сына</font> <font color="blue">Node rightSon = E'(temp) </font> <font color="green">// отдадим атрибут левого сына правому как наследуемый атрибут</font> <font color="blue">res.addChild(rightSon) </font> <font color="green">// подвешиваем правого сына сына</font> <font color="blue">res.val = res.children["E'(temp)) "].val</font>
'''break'''
'''default''' :
<font color="red">error</font>("unexpected char")
<font color="blue">res.val = E'.val</font>
'''return''' res
res.addChild(Node("+"))
res.addChild(T())
<font color="blue">temp = res.children["T"].val ADD.res = ADD(acc, temp) <font color="green">// ADD проведет вычисления из наследуемого атрибута add и атрибута ребенка "T"</font> res.addChild(E'(ADD.res))<font color="green">// результат вычислений будет передан правому ребенку как наследуемый атрибут</font> res.val = res.children["E'"].val</font>
'''break'''
'''case''' '$', ')' :
<font color="blue">res.val = acc;</font>
'''break'''
'''default''' :
res.addChild(Node("("))
res.addChild(E())
<font color="blue">rev.val = res.children["E"].val</font>
consume(')')
res.addChild(Node(")"))
Функции для $T$ и $T'$ строятся аналогично.
[[Файл:2add3add7.png|600px|center|thumb| Дерево разбора для '''$2\ +\ 3\ +\ 7$''']]
</wikitex>
==Атрибуты в ANTLR==
Общедоступный генератора генератор разборщиков ANTLR<ref>[http://www.antlr.org/ ANTLR {{---}} Parser generator]</ref> поддерживает синтаксически управляемое определение.
Рассмотрим для примера грамматику той же грамматики арифметических выражений с операторами <tex>+-\times ,\div*</tex>, скобками и выводом результата выражениаявыражения пример на ANTLR.
grammar ExprExpression;
'''@header''' { package ru.ifmo.ctddev.wiki; }
Естественным образом можно добавлять действия в продукции, где это нужно. Действия выполняются после предыдущего элемента грамматики и до следующего. Стартовый нетерминал печатает резульатрезультат: s : e expr { System.out.println($eexpr.val); }; В продукции для нетерминала <code>expr</code> определяется возвращаемое значение (<code>['''int''' val]</code>). Обращение к этому атрибуту имеет вид <code>$expr.value</code>. В фигурных скобках записаны семантические правила. Разобранные нетерминалы возвращают результат, вычисленный в поддереве(<code>returns [int val]</code>)как свой синтезируемый атрибут, процесс вычисления которого описан в фигурных скобках <code>{ $val = $exprP.val;};</code>.
Естественным образом можно добавлять действия в продукции, где это нужноНаследуемые атрибуты передаются нетерминалу как параметр(<code>exprP[$term. Действия выполняются после предыдущего элемента грамматики и до следующегоval]</code>) expr '''returns''' ['''int''' val] : term exprP[$term.val] { $val = $exprP.val; } ;
e exprP['''int''' i] '''returns''' ['''Integerint''' valueval]: t : {$value val = $t.valuei;}<font color="green"> // <tex>\varepsilon</tex>-правило</font> ( | '+' t term expr = exprP[$i + $term.val] {$value +val = $texpr.valueval;} | ; term '''returns''' ['''int''-' t val] : fact termP[$fact.val] {$value -val = $ttermP.valueval;} )*;
t termP['''int''' i] '''returns''' ['''Integer[int''' valueval]: f : {$value val = $f.valuei;} ( | '*' f fact expr = termP[$i * $fact.val] {$value *val = $fexpr.valueval;} )* ;
f fact '''returns''' ['''Integerint''' valueval]: : '(' expr '-)' f {$value val = $fexpr.valueval;} | NUM {$value val = Integer.parseInt($NUM.text);} | '(' e ')' {$value = $e.value;}
;
WS : [ \t \r \n]+ -> skip ;
NUM : [0-9]+ ;
 
 
В продукции для нетерминала '''e''' определяется возвращаемое значение (<code>[Integer value]</code>). Обращение к этому атрибуту имеет вид <code>$e.value</code>. В фигурных скобках записаны семантические правила.
== Примечания ==
Анонимный участник

Навигация