metoda_2013
.pdfТЕОРИЯ ЯЗЫКОВ ПРОГРАММИРОВАНИЯ И МЕТОДЫ ТРАНСЛЯЦИИ
|
А2 удалить два элемента «1» в |
-а |
= |
|
стек |
"1" |
|
|
|
|
|
+ |
А1 поместить «+» в стек |
|
|
|
|
|
|
b |
А1 поместить в стек «b» |
|
|
|
|
|
|
|
А3 удалить из стека три элемента; |
1 + |
b |
|
"2" в стек |
=" 2" |
|
|
|
|
|
) |
|
|
|
|
|
|
|
* |
А1 «*» в стек |
|
|
|
|
||
|
|
|
|
( |
|
|
|
|
|
|
|
с |
А1 поместить в стек « с» |
|
|
|
|
|
|
+ |
А1 поместить в стек « +» |
|
|
|
|
|
|
d |
А1 поместить в стек «d» |
|
|
|
|
|
|
|
А3 удалить из стека три элемента; |
c + d =" |
|
|
."3" в стек |
3" |
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
А3 удалить из стека три элемента; |
"2" |
*" |
|
."4" в стек |
3" = "4" |
|
|
|
|
|
|
А4 удалить из стека элемент |
|
|
|
|
|
|
Грамматики могут содержать вызов действий не только для генерации кода, но и для выполнения других задач.
Работа с таблицей символов |
|
|
Поскольку синтаксический |
анализатор |
обычно |
использует контекстно-свободную |
грамматику, |
необходимо |
найти метод определения контекстно-зависимых частей языка. Например, во многих языках идентификаторы не могут применяться, если они не описаны, и имеются ограничения в отношении способов употребления в программе значений различных типов. Для запоминания описанных идентификаторов и их типов большинство компиляторов пользуются таблицей символов.
270
ТЕОРИЯ ЯЗЫКОВ ПРОГРАММИРОВАНИЯ И МЕТОДЫ ТРАНСЛЯЦИИ
Определяющая и прикладная реализация
Когда описывается идентификатор, например int a
Мы говорим, что имеется определяющая реализация «а». Однако «а» может встречаться и в другом контексте:
а:=4 или a+b или read(a),
здесь будут прикладные реализации «а» . При обработке :
определяющей реализации идентификатора компилятор помещает объект в таблицу символов,
прикладной реализации - в таблице символов осуществляется поиск элемента, соответствующего определяющей реализации объекта, чтобы узнать его тип и (возможно) другие признаки, требующиеся во время компиляции.
Блочная структура программы
Во многих языках один и тот же идентификатор может использоваться для представления в разных частях программы различных объектов. В таких случаях структура программы помогает различать эти объекты, например
begin int a;
…
end; begin char a
. . .
end;
Таблица символов должна иметь ту же блочную структуру, что и программа, чтобы различить виды употребления одного и того же идентификатора.
Основой построения СУ -схем перевода является использование двух грамматик, с помощью которых осуществляется синхронный вывод входной и выходной цепочек. Построение транслирующих грамматик предполагает применение другого подхода, который предусматривает использование одной грамматики и разрешает включение как входных, так и выходных символов в каждое правило такой грамматики.
Определение. Транслирующей грамматикой (Т -грамматикой) называется КС-грамматика, множество терминальных символов которой разбито на множество входных символов и множество выходных символов, которые называются также символами действия.
Примером Т - грамматики может служить следующая грамматика:
271
ТЕОРИЯ ЯЗЫКОВ ПРОГРАММИРОВАНИЯ И МЕТОДЫ ТРАНСЛЯЦИИ
Г: Vтвх = {a,b,c}, Vтвых = {x,y,z}, Va = { I,A}
R = {<I> -> a<I>x<A>, <I> -> z,
<A> -> <A><C>, <A> -> by
}.
Чтобы не возникало путаницы в случае использования одинаковых символов во входном и выходном алфавитах, условимся выделять выходные символы фигурными скобками.
С использованием таких обозначений правила грамматики ГТ4.1 имеют вид:
R = {<I>->a<I>{x}<A>, <I>->{z}, <A>-><A>c, <A>->b{y}
}.
Вывод в транслирующих грамматиках выполняется по тем же правилам, что и в обычных КС - грамматиках. Например, в рассматриваемой грамматике из начального символа может быть выведена следующая цепочка:
<I> ==> a<I>{x}<A> ==> a{z}{x}<A> ==> a{z}{x]b{y}
Каждый символ или цепочка символов, заключенные в фигурные скобки, должны рассматриваться как единый символ, называемый символом действия. В общем случае цепочки символов, заключенные в фигурные скобки, можно
интерпретировать как имена процедур, выполнение которых производит требуемый эффект на выходе. При описании перевода обычно предусматривают, что каждый символ действия представляет собой процедуру, осуществляющую передачу символа, заключенного в фигурные скобки, на выход. Когда нужно подчеркнуть, что используется такая интерпретация символов действия, то Т - грамматику называют грамматикой цепочного перевода.
Входная и выходная грамматики заданной транслирующей граммагики.
Из каждой Т - грамматики можно получить две обычных грамматики, одна из которых позволяет строить входные цепочки, а другая - выходные. Правила построения таких грамматик можно сформулировать следующим образом.
272
ТЕОРИЯ ЯЗЫКОВ ПРОГРАММИРОВАНИЯ И МЕТОДЫ ТРАНСЛЯЦИИ |
|
|||
Определение. Если из правил транслирующей грамматики |
ГТ |
|||
удалить выходные символы, то получим |
входную грамматику |
|||
ГТвх для заданной грамматики. |
Если из правил заданной |
|||
транслирующей |
грамматики удалить |
входные символы, |
то |
|
получим выходную грамматику ГТвых |
заданной транслирующей |
грамматики ГТ. Язык, порождаемый грамматикой ГТвх, называется входным языком заданной транслирующей грамматики, а язык, порождаемый ГТвых , называется выходным языком заданной транслирующей грамматики ГТ.
Цепочки символов, получаемые путем вывода в Т - грамматике, содержат как символы входного алфавита, так и символы выходного алфавита - символы действия. Каждую такую цепочку можно представить как пару, состоящую из входной и выходной цепочки.
Определение. Если из цепочки символов a , полученной путем вывода в заданной Т -грамматике , исключим все выходные символы, то получим цепочку a1, которую назовем входной цепочкой. Если же из цепочки a исключим все символы входного алфавита, то в результате получим цепочку a2, которую назовем выходной цепочкой, порождаемой Т - грамматикой . Цепочки a1 и a2 образуют пару, выводимую в заданной Т - грамматике.
Построение транслирующей грамматики по СУ - схеме.
Определение. Множество пар цепочек, выводимых с помощью правил заданной Т - грамматики, образуют перевод, определяемый этой грамматикой.
Последнее определение позволяет нам сделать вывод, что один и тот же перевод может быть задан как с помощью СУ - схемы, так и с помощью Т - грамматики. Эти два способа задания являются равноправными и, более того, они допускают преобразование друг в друга.
Возможность одного из таких преобразований устанавливается следующим утверждением.
Утверждение. Для каждой простой СУ-схемы Т можно построить транслирующую грамматику ГТ такую, что переводы, порождаемые СУ - схемой и Т - грамматикой, совпадают.
C(T) = C(ГТ)
Чтобы показать справедливость этого утверждения, опишем способ построения Т - грамматики по заданной СУ - схеме. Допустим, что задана СУ - схема
Т = {Vтвх, Vтвых,Va,Q,I}
273
ТЕОРИЯ ЯЗЫКОВ ПРОГРАММИРОВАНИЯ И МЕТОДЫ ТРАНСЛЯЦИИ
и требуется построить Т - грамматику Г~ = {V'твх,V'твых,V'a,R,I}.
Для того чтобы получить тот же самый перевод, необходимо, чтобы
Vтвх = Vтвх', Vтвых = Vтвх', Va = Va'.
Рассмотрим преобразование правила из множества Q СУ - схемы A->a ,b , где цепочки
a = xоAох1А1...xnAn и b = yоAоу1A1...уnAn,
и поставим в соответствие этому правилу правило грамматики в виде:
А -> xоуоAох1у1A1...xnynAn.
Это можно сделать всегда, поскольку СУ - схема - простая и в каждом ее правиле используются одни и те же нетерминалы в одном и том же порядке. Рассмотренное построение обеспечивает включение во входную цепочку выходных символов цепочки, порождаемой СУ-схемой. Следовательно, каждый шаг вывода в Т - грамматике будет добавлять к выводимой цепочке те же символы, что и СУ - схема добавляет к выходной цепочке.
Применение описанных положений рассмотрим на примере построения Т - грамматики по СУ - схеме Т4.4.
Используя фигурные скобки для представления выходных символов и выполняя пре-образование, получаем грамматику
R = {<A> -> x{x}, <A> -> (<B>),
<B> -> <A><C>, <C> -> +<A>{+}<C>, <C> -> $
}.
Перевод цепочки ((x+x)+x) с применением правил построенной грамматики может быть получен с помощью следующего вывода:
<A> ==> <A><C> ==> (<B>)<C> ==> (<A><C>)<C> ==> ((<B>)<C>)<C> ==> ((<A><C>)<C>)<C> ==> ((x{x}<C>)<C> ==> ((x{x}+<A>{+}<C>)<C>)<C> ==>
((x{x}+x{x}{+}<C>)<C>)<C> ==>((x{x}+x{x}{+})<C>)<C> ==> ((x{x}+x{x}{+})+<A>{+}<C>)<C> ==> ((x{x}+x{x}{+})+x{x}{+}<C>)<C> ==> ((x{x}+x{x}{+})+x{x}{+})<C> ==> ((x{x}+x{x}{+})+x{x}{+}).
Исключая из полученной цепочки вначале выходные, а затем входные символы, получаем выводимую пару
((x+x)+x,{x}{x}{+}{x}{+}),
которая совпадает с результатом вывода в заданной СУ - схеме.
274
ТЕОРИЯ ЯЗЫКОВ ПРОГРАММИРОВАНИЯ И МЕТОДЫ ТРАНСЛЯЦИИ
13. Атрибутивные грамматики. Синтезируемый и наследуемый атрибуты.
Атрибутная транслирующая грамматика – это транслирующая грамматика, к которой добавляются следующие определения:
1.Каждый входной символ, символ действия или нетерминальный символ имеет конечное множество атрибутов, и каждый атрибут имеет (возможно, бесконечное) множество допустимых значений.
2.Все атрибуты нетерминальных символов и символов действия делятся на наследуемые и синтезируемые.
3.Правила вычисления наследуемых атрибутов определяются следующим образом:
каждому вхождению наследуемого атрибута в правую часть данной продукции сопоставляется правило вычисления значения этого атрибута как функции некоторых других атрибутов символов, входящих в правую или левую часть данной продукции
задается начальное значение каждого наследуемого атрибута начального символа
4.Правила вычисления синтезируемых атрибутов определяются так:
каждому вхождению синтезируемого нетерминального атрибута в левую часть данной продукции сопоставляется правило вычисления значения этого атрибута как функции некоторых других атрибутов символов, входящих в левую или правую часть данной продукции;
каждому синтезируемому атрибута символа действия сопоставляется правило вычисления значения этого
атрибута как функции некоторых других атрибутов этого символа действия.
Главная идея, лежащая в основе понятия атрибутивной грамматики, состоит в том, что значения символов сопоставляются всем вершинам дерева вывода, как терминальным, так и нетерминальным. Отношения между входными и выходными значениями выражаются по принципу “от правила к правилу”, при этом значения находятся в вершинах дерева. Атрибуты нетерминалов могут передаваться и вычисляться по дереву вывода сверху вниз (синтезируемые атрибуты) и снизу вверх (наследуемые атрибуты).
275
ТЕОРИЯ ЯЗЫКОВ ПРОГРАММИРОВАНИЯ И МЕТОДЫ ТРАНСЛЯЦИИ
Пример синтезирующего атрибута.
Предположим существует лексический блока, задающий входное множество {(,),+,*,c} , c-const для синтаксического блока допускающего выражения
7
Значение выходного символа {ОТВЕТ} должно быть числом. Требуемое отношение между значениями входных лексем и значением выходного ОТВЕТ можно выразить словами "ОТВЕТ - это числовое значение входного выражения". Найдем математическое выражение этой фразы. Рассмотрим конкретную входную цепочку: (С3 + С9) * (С2 + С41), где значения входных лексем, выданных лексическим блоком, указаны индексами. Этой входной цепочке должна соответствовать выходная цепочка ОТВЕТ516. Построим дерево вывода, соответствующее данной входной цепочке.
Каждый нетерминал этого дерева <E>, <T>, <P> помечен значением соответствующего подвыражения, а выходной символ
– требуемым выходным значением.
Чтобы определить, как расставлять значения на дереве вывода, получаемом по данной грамматике, вначале определим, как получить значение любой нетерминальной вершины, если даны значения ее прямых потомков. С этой целью сопоставим каждому правилу грамматики для <E>, <T> и <P> правило вычисления значения вершины, соответствующей нетерминалу
276
ТЕОРИЯ ЯЗЫКОВ ПРОГРАММИРОВАНИЯ И МЕТОДЫ ТРАНСЛЯЦИИ
левой части продукции, по данным значениям ее прямых потомков, соответствующих символам правой части грамматики.
Например, правило вычисления значений для продукции E
левой части равно сумме значения нетерминала <E> в правой части и значения нетерминала <E>. Чтобы применить это правило к левому, заключенному в скобки вхождению <E>, в дереве вывода примера, значение <E> в правой части равно 3, а значение <T> равно 9. Отсюда следует, что значение <E> в левой части равно 12 и можно приписать значение 12 соответствующей вершине дерева.
значение <E> равно значению <T>. Правило для продукции <T> равно произведению значения <T> в правой части и значения <P>.
состоит в том, что значение <P> равно значению с.
правилом, что значение символа ОТВЕТ равно значению <E>. Для того, чтобы выразить приведенные выше правила
математически более строго, можно в каждой продукции дать разные имена разным встречающимся в ней значениям, а затем сформулировать соответствующие ей правила при помощи этих имен.
Тогда продукции и соответствующие им правила можно записать так:
1.
2. |
E p |
<E>q + <T>r |
3. |
Ep |
q |
4. |
Tp |
q * <P>r |
5. |
Tp |
q |
6. |
Pp |
q) |
7. |
Pp |
q |
277
ТЕОРИЯ ЯЗЫКОВ ПРОГРАММИРОВАНИЯ И МЕТОДЫ ТРАНСЛЯЦИИ
Использование имен переменных локально для каждой продукции, и, если одна и та же буква p встречается в двух разных продукциях, это не имеет значения.
Приведенные выше продукции вместе с соответствующими правилами вычисления значений являются примерами “атрибутных правил”, и взятые вместе с начальным нетерминалом <S> образуют “атрибутную грамматику”. Значения вышеприведенных правил называются “атрибутами”.
В этом примере значение атрибута каждого нетерминала определяется символами, расположенными в дереве вывода под этим нетерминалом. Такое “восходящее” вычисление выражается в том, что правила вычисления атрибутов нетерминалов, ассоциированные с продукциями, указывают, как вычислять атрибуты в левой части продукции по данным атрибутам символов правой части. Атрибуты, значения которых получаются таким восходящим способом, то есть снизу вверх, называются “синтезируемыми ” атрибутами.
Пример наследуемого атрибута.
Рассмотрим следующую грамматику:
1.<описание>::=type id <список переменных>
2.<список переменных>::=, id <список переменных>
3.<список переменных>::=e /пустое правило/
Предположим, что существует лексический блок, задающий три лексемы id ТИП, где лексема id обозначает идентификатор и ее значение является указателем на соответствующий этому идентификатору элемент таблицы имен. ТИП – лексема со значением, определяющим, какой из типов, ВЕЩЕСТВЕННЫЙ,
278
ТЕОРИЯ ЯЗЫКОВ ПРОГРАММИРОВАНИЯ И МЕТОДЫ ТРАНСЛЯЦИИ
ЦЕЛЫЙ или ЛОГИЧЕСКИЙ, должен быть поставлен в соответствии идентификаторам из данного списка.
При обработке описания каждой переменной синтаксический блок вызывает процедуру УСТАНОВИТЬ_ТИП, которая помещает один из типов ВЕЩЕСТВЕННЫЙ, ЦЕЛЫЙ или ЛОГИЧЕСКИЙ в надлежащее поле таблицы символов. Вызов процедуры УСТАНОВИТЬ_ТИП лучше всего осуществлять сразу после того как, идентификатор поступил на блок синтаксического анализатора. Это можно описать следующей грамматикой, использующей символ действия:
1.<описание>::=type id {УСТАHОВИТЬ_ТИП} <список переменных>
2.<список переменных>::=, id {УСТАHОВИТЬ_ТИП} <список переменных>
3.<список переменных>::=e /пустое правило/
Пусть процедура УСТАНОВИТЬ_ТИП имеет два аргумента: это идентификатор (или его адрес) и тип. Тогда вызов процедуры УСТАHОВИТЬ_ТИП можно записать УСТАНОВИТЬ_ТИП (id, type). Введем в вышеприведенную грамматику атрибуты и правила их вычисления, чтобы в последовательностях актов входные символы были представлены вместе с их значениями, играющими роль атрибутов, а вхождения символов действия {УСТАНОВИТЬ_ТИП} имели по два атрибута, представляющих аргументы соответствующего вызова процедуры УСТАНОВИТЬ_ТИП. Тогда вхождения УСТАНОВИТЬ_ТИП будут
иметь такой вид: {УСТАНОВИТЬ_ТИП}id, type.
Рассмотрим, как УСТАНОВИТЬ_ТИП получает свои атрибуты. В правиле 1 это делается просто, так как атрибуты можно получить используя входные символы type и id, входящие в это правило. В правиле 2 type не доступен, его нужно передать используя атрибуты нетерминала. Поэтому нетерминал <список переменных> должен иметь атрибут, который будет представлять тип.
Таким образом, грамматика перепишется :
1.<описание>::=typet idp {УСТАHОВИТЬ_ТИП}p1, t1 <список переменных> t2
2.<список переменных> t ::=, id p {УСТАHОВИТЬ_ТИП}p1, t1 <список переменных> t2
3.<список переменных> t ::=e /пустое правило/
279