Изменения

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

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

1620 байт убрано, 09:32, 4 июня 2015
Нет описания правки
{{Определение
|definition =
'''Атрибут''' {{---}} дополнительные данные, ассоциированные с грамматическими символами.Если $X$ представляет собой символ, а $a$ — один из его атрибутов, то значение а в некотором узле дерева разбора, помеченном $X$, записывается как $X.a$. Если узлы дерева разбора реализованы в виде записей или объектов, то атрибуты X могут быть реализованы как поля данных в записях, представляющих узлы $X$. Атрибуты могут быть любого вида: числами, типами, таблицами ссылок или строками. Атрибуты делятся на '''наследуемые''' и '''синтезируемые'''.
}}
$
После В примерах для нисходящего разборщика [[Устранение_левой_рекурсии| устранения левой рекурсииустраним левую рекурсию]] имеем следующее:
$
НапримерСтоит отметить, атрибутом терминала что не существует гарантии наличия даже одного порядка обхода для вычисления всех атрибутов в узлах дерева разбора. Рассмотрим, например, нетерминалы $nA$ будет число, которое он представляети $B$: {| style="background-color:#CCC;margin: ($n0.val 5px"!style="background-color:#EEE"| продукции!style= 123$). Атрибутами нетерминалов будут значения, вычисленные в поддереве."background-color:#EEE"| Семантические правила В процессе трансляции происходит присваивание атрибутов одних элементов грамматики другим элементам грамматики, при этом присваивание может происходить более сложным способом, чем простое копирование. В некоторых случаях могут быть нужны какие|-то побочные эффекты, например вывод кода или взаимодействие с глобальным контекстом. Для этого нужны транслирующие символы. Заменим правило |style="background-color:#FFF;padding:2px 30px"| $T A \rightarrow T*Fto B$ на следующее|style="background-color:#FFF;padding: 2px 30px"| $T \rightarrow T*F\{T_0A.vals =T_1.val*FB.vali \}$, где $\{T_0B.vali =T_1.val*FA.val\}s+1$ {{---|}} транслирующий символ. Аналогично к Данные правила циклические; невозможно вычислить ни $F \rightarrow n$ добавляется $\{F.val = nA.val\}s$. Часто бывает неоптимально для каждого действия заводить транслирующий символ(добавляется лишний нетерминал в грамматику). Поэтому при простом присваивании(копировании) атрибутов разрешают не выносить транслирующий символ и заменяют его списком действийузле, привязанным к правилу. Обычно, транслирующий символ может работать только со своими атрибутамини $B. Тогда перепишем правило $T \rightarrow T*Fi$ в виде: $T \rightarrow T*F\ \{MUL \ res = op_1*op_2\}$ И ассоциируем с ним следующий список действийдочернем узле, которые будут выполняться в особом порядке, рассмотренном далеене зная значение другого атрибута. $ MUL.op_1 = T_1.val \\ MUL.op_2 = F.val \\ T_0.val = MULДалее будет рассмотрено два класса синтаксически управляемых грамматик, для которых можно однозначно определить порядок вычисления атрибутов.res $
Атрибуты делятся на '''наследуемые''' и '''синтезируемые'''.
=Синтезируемые атрибуты=
==Пример S-атрибутной грамматики.==
Выпишем синтексическ синтексически управляемое определение для грамматики арифметических выражений с операторами $+$ и $*$:
{| style="background-color:#CCC;margin:0.5px"
|}
В нашем примере видно, что $.val$ зависит только от детей, то есть это синтезируемый атрибут. Результат умножителя ($MUL.res$) зависит только от атрибутов атрибутов самого умножителя ($MUL.op_1$ и $MUL.op_2$), а значит тоже является синтезируемым(аналогично с сумматором $ADD$).
<картинка>
После такого разбора, в $S.val$ будет лежать вычисленное значение выражения. Можно, например сразу напечатеть его, добавив правило к нему правило $\{print(S.val)\}$.
Хотя всегда можно переписать синтаксически управляемое определение таким образом, чтобы использовать только синтезируемые атрибуты, зачастую более удобно и естественно воспользоваться также и наследуемыми атрибутами.
|-
|style="background-color:#FFF;padding:2px 30px"| $D \to TL$
|style="background-color:#FFF;padding:2px 30px"| $L.in inh = T.type$
|-
|style="background-color:#FFF;padding:2px 30px"| $T \to int$
|-
|style="background-color:#FFF;padding:2px 30px"| $L \to L,id\ \{ENTRY addtype(key, value)\}$
|style="background-color:#FFF;padding:2px 30px"| $L_1.in=L.in inh \\ ENTRY.key=id.entry \\ ENTRY.value=L.ininh$
|-
|style="background-color:#FFF;padding:2px 30px"| $L.id \to id\ \{ENTRY addtype(key, value)\}$
|style="background-color:#FFF;padding:2px 30px"| $ENTRY.key=id.entry \\ ENTRY.value=L.ininh$
|}
Семантическое правило $L.in inh = T.type$, связанное с продукцией $D \to TL$, определяет наследуемый атрибут $L.ininh$ как тип объявления. Затем приведенные правила распространяют этот тип вниз по дереву разбора с использованием атрибута $L.ininh$. Транслирующий символ ENTRY, связанный с продукциями для $L$, вызывает процедуру $addtype$ для добавления типа каждого идентификатора к его записи в таблице символов (по ключу, определяемому атрибутом $entry$).
<картинка>
</wikitex>
=Аспекты реализацииАтрибуты в ANTLR=Общедоступный генератора разборщиков ANTLR<ref>[http://www.antlr.org/ ANTLR {{---}} Parser generator]</ref> поддерживает синтаксически управляемое определение. Рассмотрим для примера грамматику арифметических выражений.
Рассмотрим Вне продукций граматики бывает нужно вставить в сгенерированный разборщик(пример для Java) package или import, а также некоторые аспекты реализации на более сложном примереполя и методы.Это делается с помощью @header и @members:
@header { package tools; import java.util.*; }
@parser::members {
Map<String, Integer> memory = new HashMap<String, Integer>();
int eval(int left, int op, int right) {
...
}
}
При реализации методом рекурсивного спускаЕстественным образом можно добавлять действия в продукции, каждому нетерминалу соответствует рекурсивная функция, которая ранее возвращала дерево разбора соответствующего узлагде это нужно: stat: e NEWLINE {System.out. Теперь же функция, соответствующая некоторому нетерминалу println($Te.val);} | ID '=' e NEWLINE {memory.put($ID.text, будет принимать в качестве аргументов его наследуемые атрибуты, а возвращать его синтезируемые атрибуты$e.val);} | NEWLINE ;
T(): '''int''' T_1Действия выполняются после предыдущего элемента грамматики и до следующего.val=T() consume('*') FВ данном примере действия добавлены на конце альтернативы, поэтому действие выполнится после того, как парсер сматчит все выражение.val=F() MUL.res = MUL(op1=T1.valКогда парсер встречает выражение, за которым идет символ новой строки, op2=Fему нужно напечатать результат.val) '''return''' MULКогда он встречает присваивание - ему нужно записать имя и значение переменной в память.res
Заметим, что в рассматриваемой грамматике присутствует левая рекурсия, в связи с чем некоторые наши примеры имеют искусственный характер, хотя и являются наглядными.
Рассмотрим, что происходит с атрибутами при [[Устранение_левой_рекурсии| устранения левой рекурсии]] из грамматики.
Пусть есть правила:
$
x\ A \to A \alpha \Leftrightarrow x=g(A_1, \alpha) \\
x\ A \to \beta \Leftrightarrow x=f(\beta)
$
 
<картинка>
После устранения левой рекурсии:
 
$
x\ A \to \beta A' \\
x\ A' \to \alpha A' \\
x\ A \to \varepsilon
$
 
<картинка>
 
==Более сложный пример==
 
Рассмотрим грамматику арифметических выражений, для наглядности оставив только числа, сложение и скобки.
 
<картинки деревьев и псевдокод функций для грамматики до и после устранения левой рекурсии.>
 
<также пример генерации ассемблерного кода для стековой и регистровой машины.>
 
 
хоть немного про ANTLR - атрибуты.
 
 
</wikitex>
== Примечания ==
<references/>
= Источники информации =
* Альфред Ахо, Рави Сети, Джеффри Ульман. Компиляторы. Принципы, технологии, инструменты. Издательство Вильямс. Первое издание. 2003. Стр. 279 {{---}} 305.
* Альфред Ахо, Рави Сети, Джеффри Ульман. Компиляторы. Принципы, технологии, инструменты. Издательство Вильямс. Второе издание. 2008. Стр. ??? 383 {{---}} ???398.
* [https://theantlrguy.atlassian.net/wiki/display/ANTLR4/Parser+Rules#ParserRules-RuleAttributeDefinitions| ANTLR Documentation - Rule Attribute Definitions]
[[Категория: Методы трансляции]]
[[Категория: Нисходящий разбор]]
497
правок

Навигация