Главная страница

Учебное пособие для студентов II курса (издание третье, переработанное и дополненное) Москва 2009 удк 519. 682. 1


Скачать 2.2 Mb.
НазваниеУчебное пособие для студентов II курса (издание третье, переработанное и дополненное) Москва 2009 удк 519. 682. 1
Анкорformal.grammars.and.languages.2009.pdf
Дата18.03.2019
Размер2.2 Mb.
Формат файлаpdf
Имя файлаformal.grammars.and.languages.2009.pdf
ТипУчебное пособие
#26014
страница11 из 15
1   ...   7   8   9   10   11   12   13   14   15

Замечание
1. Для интерпретации, кроме ПОЛИЗа выражения, необходима дополнительная информация об операндах, хранящаяся в таблицах.
2. Может оказаться так, что знак бинарной операции по написанию совпадает со знаком унар- ной; например, знак «

» в большинстве языков программирования означает и бинарную операцию вычи- тания, и унарную операцию изменения знака. В этом случае во время интерпретации операции «

» воз- никнет неоднозначность: сколько операндов надо извлекать из стека и какую операцию выполнять.
Устранить неоднозначность можно, по крайней мере, двумя способами:

заменить унарную операцию бинарной, т. е. считать, что

а означает 0

а;

либо ввести специальный знак для обозначения унарной операции; например,

а заменить на

a. Важно отметить, что это изменение касается только внутреннего представления про- граммы и не требует изменения входного языка.

Элементы теории трансляции
/
Генерация внутреннего представления программ
84
Оператор присваивания
I :

E в ПОЛИЗе будет записан как
I E :

где «:

» — это двухместная операция, а I и Е — ее операнды; подчеркнутое I означает, что операндом операции «:

» является адрес переменной I, а не ее значение.
Оператор перехода в терминах ПОЛИЗа означает, что процесс интерпретации надо продолжить с того элемента ПОЛИЗа, который указан как операнд операции перехода.
Чтобы можно было ссылаться на элементы ПОЛИЗа, будем считать, что все они пере- нумерованы, начиная с 1 (допустим, занесены в последовательные элементы одномерного массива).
Пусть ПОЛИЗ оператора, помеченного меткой L, начинается с номера p, тогда опера- тор перехода gotoL в ПОЛИЗе можно записать как
p! где «!» — операция выбора элемента ПОЛИЗа, номер которого равен p.
Немного сложнее окажется запись в ПОЛИЗе условных операторов и операторов
цикла.
Например, если рассматривать оператор if B then S
1
else S
2
как обычную трехместную операцию с операндами B, S
1
, S
2
, то ПОЛИЗ такого оператора должен выглядеть примерно так: BS
1
S
2
if, где B′ — ПОЛИЗ условия, S
1
S
2
′ — ПОЛИЗ операторов S
1
, S
2,
ifобозначе- ние условной операции. Но тогда при интерпретации ПОЛИЗа обе ветви S
1
, S
2
заранее вы- числяются, независимо от условия B, что не соответствует семантике условного оператора.
Для корректной реализации в ПОЛИЗе управляющих конструкций if, while и т. п., их сначала заменяют эквивалентными фрагментами при помощи операторов перехода. Введем вспомо- гательную операцию — условный переход «по лжи» с семантикой
if (!B) goto L
Это двухместная операция с операндами B и L. Обозначим ее !F, тогда в ПОЛИЗе она будет записана как
Bp !F, где p — номер элемента, с которого начинается ПОЛИЗ оператора, помеченного меткой L,
B′ — ПОЛИЗ логического выражения В.
Семантика условного оператора
if Е then S
1
else S
2
с использованием введенной операции может быть описана так:
if (! Е) goto L
2
; S
1
; goto L
3
; L
2
: S
2
; L
3
: ...
Тогда ПОЛИЗ условного оператора будет таким (порядок операндов — прежний!):
Е p
2
!F S
1

p
3
! S
2
′ ..., где p
i
— номер элемента, с которого начинается ПОЛИЗ оператора, помеченного меткой L
i
,
i

2,3, Е′ — ПОЛИЗ логического выражения Е. Заметим, что расположение внутренних

Элементы теории трансляции
/
Генерация внутреннего представления программ
85 конструкций — условия Е и операторов S
1
, S
2
— относительно друг друга не изменилось.
Это обязательное требование к ПОЛИЗу управляющих конструкций.
Семантика оператора цикла whileЕ doSможет быть описана так:
L
0
: if (! Е) goto L
1
; S; goto L
0
; L
1
: ... .
Тогда ПОЛИЗ оператора цикла while будет таким (порядок операндов — прежний!):
Еp
1
!F Sp
0
! ..., где p
i
— номер элемента, с которого начинается ПОЛИЗ оператора, помеченного меткой L
i
,
i

0,1, Е′ — ПОЛИЗ логического выражения Е.
Операторы ввода и вывода М-языка являются одноместными операциями.
Оператор ввода read(I) в ПОЛИЗе будет записан как I read;
Оператор вывода write(E) в ПОЛИЗе будет записан как Е write, где Е′ — ПОЛИЗ выражения Е.
Постфиксная польская запись операторов обладает всеми свойствами, характерными для постфиксной польской записи выражений, поэтому алгоритм интерпретации выражений пригоден для интерпретации всей программы, записанной в ПОЛИЗе (нужно только расши- рить набор операций; кроме того, выполнение некоторых из них не будет давать результата, записываемого в стек).
Постфиксная польская запись может использоваться не только для интерпретации промежуточной программы, но и для генерации по ней объектной программы. Для этого в алгоритме интерпретации вместо выполнения операции нужно генерировать соответствую- щие команды объектной программы.
Синтаксически управляемый перевод
На практике синтаксический, семантический анализ и генерация внутреннего пред- ставления программы часто осуществляются одновременно.
Существует несколько способов построения промежуточной программы. Один из них, называемый синтаксически управляемым переводом, особенно прост и эффективен.
В основе синтаксически управляемого перевода лежит уже известная нам грамматика с действиями (см. раздел о контроле контекстных условий). Теперь, параллельно с анализом исходной цепочки лексем, будем выполнять действия по генерации внутреннего представле- ния программы. Для этого дополним грамматику вызовами соответствующих процедур гене- рации.
Содержательный пример — генерация внутреннего представления программы для М- языка — приведен ниже, а здесь в качестве иллюстрации рассмотрим более простой пример.
Пусть есть грамматика, описывающая простейшее арифметическое выражение.
G
expr
:
E T {

T }
T F {

F }
F

a | b | (E)
Тогда грамматика с действиями по переводу этого выражения в ПОЛИЗ будет такой.

Элементы теории трансляции
/
Генерация внутреннего представления программ
86
G
expr_polish
:
E T {

T

cout

'

';

}
T F {

F

cout

'

';

}
F

a

cout

'a';

| b

cout

'b';

| (E)
В процессе анализа методом рекурсивного спуска входной цепочки a

b

с по грам- матике G
expr_polish
в выходной поток будет выведена цепочка a b с


, являющаяся польской записью исходной цепочки. Таким образом, данная грамматика с действиями каждой цепоч- ке языка арифметических выражений ставит в соответствие подходящую цепочку языка польских записей арифметических выражений. Иными словами, такая грамматика задает
перевод из одного формального языка в другой.
Определение: Пусть T
1
и T
2
— алфавиты. Формальный перевод

— это подмноже- ство множества всевозможных пар цепочек в алфавитах T
1
и T
2
:


(T
1
*

T
2
*
).
Назовем входным языком перевода

язык L
вх
(

)

{

|


: (

,

)


}.
Назовем целевым (или выходным) языком перевода

язык L
ц
(

)

{

|


: (

,

)


}.
Перевод

неоднозначен, если для некоторых


T
1
*
,

,


T
2
*
,



, (

,

)


и
(

,

)


.
Рассмотренная выше грамматика G
expr_polish
задает однозначный перевод: каждому вы- ражению ставится в соответствие единственная польская запись. Неоднозначные переводы могут быть интересны при изучении моделей естественных языков; для трансляции языков программирования используются однозначные переводы.
Заметим, что для двух заданных языков L
1 и L
2 существует бесконечно много фор- мальных переводов
25)
. Чтобы задать перевод из L
1 в L
2
, важно точно указать закон соответ-
ствия между цепочками L
1 и L
2
Пример. Пусть L
1

{0
n
1
m
| n

0, m

0} — входной язык, L
2

{a
m
b
n
| n

0, m

0} — выходной язык и перевод

определяется так: для любых n

0, m

0 цепочке
0
n
1
m

L
1
соответствует цепочка a
m
b
n

L
2
. Можно записать

с помощью теоретико- множественной формулы:


{ (0
n
1
m
, a
m
b
n
) | n

0, m

0 }, L
вх
(

)

L
1
, L
ц
(

)

L
2
Задача.
Реализовать перевод


{ (0
n
1
m
, a
m
b
n
) | n

0, m

0}грамматикой с действиями.
Решение
Язык L
1
можно описать грамматикой:
S
0S | 1A
A →

1A |

Вставим действия по переводу цепочек вида 0
n
1
m
в соответствующие цепочки вида
a
m
b
n
:
S
0S

cout

'b';

| 1

cout

'a';

A
A →

1

cout

'a';

A |

25)
При условии, что хотя бы один из языков
L
1
, L
2
бесконечен. Действительно, пусть
L
1

{a
n
| n

0},
L
2

{b, c}
. Существует бесконечно много переводов

i

{(a
i
, b)}

{(a
n
, c) | n

i, i

0}, где
L
вх
(

i
)

L
1
, L
ц
(

i
)

L
2

Элементы теории трансляции
/
Генерация внутреннего представления программ
87
Теперь при анализе методом рекурсивного спуска цепочек языка L
1
с помощью дей- ствий будут порождаться соответствующие цепочки языка L
2
Генератор внутреннего представления программы на
М
- языке
Каждый элемент в ПОЛИЗе — это лексема, т. е. пара вида

тип_лексемы, значе-
ние_лексемы

. При генерации ПОЛИЗа будем использовать дополнительные типы лексем
(для внутреннего представления операторов):

POLIZ_GO — «!»;

POLIZ_FGO — «!F»;

POLIZ_LABEL — для ссылок на номера элементов ПОЛИЗа;

POLIZ_ADDRESS — для обозначения операндов-адресов (например, в ПОЛИЗе оператора присваивания).
Будем считать, что генерируемая программа размещается в объекте prog класса
Poliz, заданном описанием:
Poliz prog (1000); class Poliz
{ lex *p; int size; int free; public:
Poliz ( int max_size )
{ p
= new Lex [size = max_size]; free = 0;
};

Poliz() { delete []p; }; void put_lex(Lex l) { p[free]=l; ++free; }; void put_lex(Lex l, int place) { p[place]=l; }; void blank() { ++free; }; int get_free() { return free; }; lex& operator[] ( int index )
{ if (index > size) throw "POLIZ:out of array"; else if ( index > free ) throw "POLIZ:indefinite element of array"; else return p[index];
}; void print()
{ for ( int I = 0; i < free; ++i ) cout << p[i];
};
};

Элементы теории трансляции
/
Генерация внутреннего представления программ
88
Объект prog для хранения внутреннего представления программы разместим в от- крытой части класса Parser: class Parser
{
Lex curr_lex; public:
Poliz prog;
Parser (const char *program ) : scan (program),prog (1000) {} void analyze();
};
Генерация внутреннего представления программы будет проходить во время синтак- сического анализа параллельно с контролем контекстных условий, поэтому для генерации можно использовать информацию, собранную синтаксическим и семантическим анализато- рами; например, при генерации ПОЛИЗа выражений можно воспользоваться содержимым стека, с которым работает семантический анализатор. Кроме того, можно дополнить функ- ции семантического анализа действиями по генерации.
Добавим в конец тела функции check_op(
) оператор prog.put_lex (Lex (op) ); запи- сывающий в ПОЛИЗ знак операции op, а в конец функции check_not(
) добавим оператор
prog.put_lex (Lex (LEX_NOT) ); записывающий лексему not (во внутреннем представлении) в
ПОЛИЗ.
Тогда грамматика, содержащая действия по контролю контекстных условий и перево- ду выражений модельного языка в ПОЛИЗ, будет такой:
E → E
1
| E
1
[

|

|

]

st_lex.push (c_type )

E
1

check_op (
)

E
1


T { [

|

| or ]

st_lex.push (c_type )

T

check_op (
)

}
T
→ F { [

| / | and ]

st_lex.push (c_type )

F

check_op (
)

}
F → I

check_id (
); prog.put_lex ( curr_lex );

|
N

st_lex.push (LEX_INT); prog.put_lex ( curr_lex );

|
[ true | false ]

st_lex.push (LEX_BOOL); prog.put_lex ( curr_lex );

|
not F

check_not(
);

| (E)
Действия, которыми нужно дополнить правило вывода оператора присваивания, так- же достаточно очевидны:
S
I

check_id (
); prog.put_lex ( Lex ( POLIZ_ADDRESS, c_val ) );

:

E

eqtype (
); prog.put_lex ( Lex ( LEX_ASSIGN ) );

При генерации ПОЛИЗа выражений и оператора присваивания элементы объекта prog заполнялись последовательно.
Семантика условного оператора
if E thenS
1
else S
2
такова, что значения операндов для операций безусловного перехода и пе- рехода «по лжи» в момент генерации операций еще неизвестны:
if (!E) goto l
2
; S
1
; goto l
3
; l
2
: S
2
; l
3
:…

Элементы теории трансляции
/
Генерация внутреннего представления программ
89
Поэтому придется оставлять соответствующие этим операндам элементы объекта
prog незаполненными и запоминать их номера, а затем, когда станут известны их значения, заполнять пропущенное.
Тогда грамматика, содержащая действия по контролю контекстных условий и перево- ду условного оператора модельного языка в ПОЛИЗ, будет такой:
S
if E

eqbool(
); pl
2

prog.get_free(
); prog.blank(
);
prog.put_lex ( Lex (POLIZ_FGO ) );

then S
1

pl
3

prog.get_free(
); prog.blank(
);
prog.put_lex ( Lex (POLIZ_GO) );
prog.put_lex ( Lex (POLIZ_LABEL, prog.get_free(
) ), pl
2
);

else S
2

prog.put_lex ( Lex (POLIZ_LABEL, prog.get_free(
) ), pl
3
);

Семантика оператора цикла while E do Sописывается так:
L
0
:
1   ...   7   8   9   10   11   12   13   14   15


написать администратору сайта