Скачиваний:
13
Добавлен:
01.05.2014
Размер:
1.43 Mб
Скачать

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

Для определенности будем считать:

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

  • выполняется цепочечный перевод, причем операционный символ вида {OUT:= “str”} означает запись в выходную ленту OUT цепочки символов “str”.

Определим действия, выполняемые первым по порядку элементом перевода (операционным символом), и правило, в которое он должен быть помещен. Для этого еще раз рассмотрим неформальное описание перевода (рис. 4.1). Очевидно, что начать запись в выходную ленту процессор сможет только тогда, когда ему будет доступна переменная цикла id. Когда процессор выполняет свертку, используя правило грамматики с номером (3), то id входит в основу и доступен процессору. Следовательно, в это правило мы можем включить операционный символ, который записывает на выходную ленту начальный участок перевода, вплоть до символа cns. Правило транслирующей грамматики с номером (3) будет выглядеть следующим образом:

(3) D id = cns { OUT:= “id := cns; m1 : if id >” } .

При выполнении свертки по этому правилу, операционный символ записывает на выходную ленту переменную цикла id. Запись этой переменной в OUT может быть выполнена функцией “Читать третий сверху символ магазина процессора и записать его на выходную ленту”. Аналогичные действия (“Читать верхний символ магазина и записать его на выходную ленту”) выполняются для начального значения цикла cns. Остальные символы не зависят от входной цепочки (терминалы выходного языка) и просто помещаются на выходную ленту.

Рассуждая подобным образом, мы получим следующие правила транслирующей грамматики (в порядке их использования при анализе входной цепочки и построении выходной цепочки), содержащие элементы перевода:

(4) E to cns { OUT:= “cns then goto m2;” }

(7) B term { OUT:= “term;” }

  1. F step cns { OUT:= “id := id +cns ; goto m1; m2 : ” }.

Добавив остальные правила грамматики, получим следующую транслирующую грамматику:

(1) S A B C

(2) A for D E F

(3) D id = cns { OUT:= “id := cns; m1 : if id >” }

(4) E to cns { OUT:= “cns then goto m2;” }

(5) F step cns { OUT:= “id := id +cns ; goto m1; m2 : ” }.

(6) B S

(7) B term { OUT:= “term;” }

  1. C next id

Для тестирования грамматики построим перевод входной цепочки

for id = cns1 to cns2 step cns3 term next id .

Синтаксическое дерево разбора этой цепочки приведено на рис. 4.2 (слева приведены номера правил грамматики, соответствующие вершинам дерева).

При восходящих методах синтаксического анализа [8] строится обращенный правый вывод (разбор) цепочки – последовательность номеров правил, используемых при свертках, который для рассматриваемого примера равен: (3, 4, 5, 2, 7, 8, 1). При этом на выходной ленте формируется цепочка:

id := cns1; m1: if id > cns2 then goto m2; id := id + cns3; goto m1; m2: term;”.

Анализ полученной цепочки позволяет сделать следующие выводы:

1. Тело цикла (терминальный оператор term) включается неправильно (не перед увеличением переменной цикла на значение шага, а в конец выходной цепочки). Это связано с тем, что правило 5 применяется раньше, чем правило 7.

2 2. При использовании правила (5) значение переменной цикла id недоступно (id вытолкнут из магазина при свертке по третьему правилу грамматики). Значение id должно быть передано в элемент перевода правила (5) для правильной генерации выходной цепочки.

3. При переводе вложенных циклов возникнет конфликт меток m1 и m2, так как m1 и m2 – фиксированные значения. Значения меток должны генерироваться при выполнении перевода и передаваться от места возникновения метки к месту ее использования.

Ошибки, обнаруженные в выходной цепочке, можно легко устранить, расширив транслирующую грамматику до атрибутной транслирующей грамматики. Первую ошибку можно исправить несколькими способами:

А. Включить в правило (4) атрибут, в который записать координаты выходной цепочки, куда должно быть включено тело цикла, передать значение этого атрибута в правило (5) и использовать его для выполнения вставки тела цикла вовнутрь выходной цепочки.

Б. Связать с нетерминалом F атрибут, значением которого является строка символов. При использовании правила (5) записать в эту строку операторы, реализующие изменение переменной цикла, затем передать значение этого атрибута в правило (1), где и переписать его значение в выходную цепочку.

В. Пополнить атрибутный процессор, выполняющий перевод, дополнительной лентой, на которую записывать операторы, реализующие изменение переменной цикла в правиле (5). В конце перевода при свертке по правилу (1) сцепить вспомогательную ленту с основной.

Применение первых двух способов не вызывает принципиальных затруднений, но неэффективно при реализации процессора, поэтому используем последний способ устранения ошибки: на выходную ленту OUT1 будем записывать операторы установки начального значения переменной цикла, проверки завершения цикла и тело цикла, а на дополнительную ленту OUT2 – операторы изменения переменной цикла с заданным шагом.

Вторая и третья ошибки устраняются стандартным способом: включением в правила грамматики атрибутов и функций их вычисления.

Преобразуем транслирующую грамматику в транслирующую атрибутную грамматику.

Рассмотрим правило транслирующей грамматики:

  1. D id = cns { OUT1:= “id := cns ; m1 : if id >” }.

При выполнении свертки по этому правилу значения терминальных символов id и cns требуется передать из входной цепочки в выходную. Это реализуется передачей соответствующих атрибутов в операционный символ и использованием их при построении выходной цепочки:

  1. D id a1 = cns a2 { OUT1:= “id n1 := cns n2 ; m1: if id >” } n1, n2

n1 a1; n2 a2

Запись “id n1в операционном символе означает, что в данное место выходной цепочки нужно поместить идентификатор, значение которого определяется унаследованным атрибутом n1 (значения id и cns – это не числовые значения этих объектов, а ссылки на таблицы, которые содержат необходимую информацию).

Так как переменная цикла id используется и в других правилах грамматики (правила (5) и (8)), то ее значение нужно передать в эти правила, связав с символом D атрибут и определив функцию для его вычисления. Окончательно правило грамматики примет вид:

(3) D s1 id a1 = cns a2 { OUT1:= “id n1 := cns n2 ; m1: if id >” } n1, n2

s1, n1 a1; n2 a2

Для определения способа передачи значения переменной цикла id (атрибут a1) в правила (5) и (8) грамматики, рассмотрим фрагмент дерева грамматики, изображенный на рис. 4.3.

S

2

AS2

B

Cn1

1

3

2

DS1

Fn1

E

next

id

{…}n2

for

(8)

(2)

1

3

id a1

cns a2 {…} step cns {…} n2

=

(5)

(3)

Рис. 4.3

Процесс передачи значения лексемы id (атрибут a1) из правила (3) грамматики отмечен на рис. 4.3 сплошными линиями.

  1. Из правой части правила (3) значение атрибута входного символа a1 присваивается синтезированному атрибуту s1 символа D из левой части этого же правила.

  2. Из правой части правила (2) значение синтезированного атрибута s1 нетерминального символа D присваивается унаследованному атрибуту n1 нетерминала F из правой части этого же правила.

  3. Из левой части правила (5) значение унаследованного атрибута n1 нетерминала F передается в правую часть унаследованному атрибуту n2 операционного символа.

Соответствующие описанному процессу правила АТ-грамматики имеют вид:

(3) D s1 id a1 = cns a2 { OUT1 := “id n1 := cns n2 ; m1: if id >” } n1, n2

s1, n1 a1; n2 a2

(2) A for Ds1 E Fn1

n1 s1

(5) Fn1 step cns { OUT2:= “ id := id +cns; goto m1; m2 : ”} n2

n2 n1

Передача значения переменной цикла в восьмое правило грамматики изображено на рис. 4.3 штриховыми линиями и описывается следующими правилами АТ-грамматики:

(2) A s2 for Ds1 E F n1

s2, n1 s1

  1. S A s2 B C n1

n1 s2

(8) C n1 next id { if n2 n3 then Error }n2

n2 n1

Особенностью правила (8) построенной АТ-грамматики является то, что его операционный символ должен интерпретироваться не как элемент цепочечного перевода, а как выполнение действий, впрямую не связанных с генерацией выходной цепочки. В этот операционный символ передаются значения переменной цикла из заголовка цикла и оператора его завершения. Если эти значения не равны, то должно возникать состояние ошибки процессора (Error).

Процесс генерации и передачи меток иллюстрируется рис. 4.4. Метки

A

3

3

Ds1, s2

F n1, n2 , n3

E s3

for

(2)

4

2

4

id = cns {…Label s3…}n3

step cns {…Label n5 …Label n4…} n4, n5

(3)

(5)

2

1

to cns{ …Label s1…} n2

(4)

1

Рис. 4.4

m1 и m2 генерируются в операционных символах правил (3) и (4) грамматики соответственно с помощью процедуры-функции NewLabel, возвращающей при обращении к ней уникальное значение метки. Правила АТ-грамматики, описывающие процесс генерации меток, имеют вид:

(3) D s1, s2 id a1 = cns a2

{ OUT1:= “id n1 := cns n2; Label s3: if id >” } n1, n2, n3

s1, n1 a1; n2 a2; s3 NewLabel; n3 s3; s2 n3;

(4) Es3 to cns { OUT1:= “cns then goto Label s1;” } n2

s1 NewLabel; n2 s1; s3 n2;

(2) A for D s1, s2 Es3 Fn1, n2, n3

n1 s1; n3 s3

(5) Fn1, n2, n3 step cns

{ OUT2:= “id := id + cns; goto Label n5; Label n4:” } n4, n5

n4 n2; n5 n3

Сцепление выходных лент OUT1 и OUT2 должно быть выполнено один раз сразу после анализа и перевода всей входной цепочки. Для реализации сцепления выходных лент грамматику необходимо пополнить новым (нулевым) правилом, включив в него операционный символ, выполняющий действия по конкатенации выходных лент. Результирующая АТ-грамматика будет иметь вид:

  1. S0 S { OUT1:=OUT1|| OUT2 }

  2. S A s1 B Cn1

n1 s1

(2) A s4 for D s1, s2 Es3 Fn1, n2, n3

s4, n1 s1; n2 s2; n3 s3;

(3) D s1, s2 id a1 = cns a2

{ OUT1:= “id n1 := cns n2; Label s3: if id >” } n1, n2, n3

s1, n1 a1; n2 a2; s3 NewLabel; n3 s3; s2 n3;

(4) Es3 to cnsa1 { OUT1:= “cnsn1 then goto Labels1;” } n1, n2

n1 a1; s1 NewLabel; n2 s1; s3 n2;

(5) Fn1, n2, n3 step cnsa1

{ OUT2:= “idn6 := idn6 + cnsn7; goto Label n5; Label n4:” }n4,n5,n6

n4 n2; n5 n3; n6 n1; n7 a1;

  1. B S

  2. B term; { OUT1:= “term; }

(8) C n1 next ida1 { if n2 n3 then Error }n2, n3

n2 n1; n3 a1

При переводе вложенных операторов цикла использование двух выходных лент в процессоре и их сцепление при выполнении перевода может привести к ошибкам в структуре выходной цепочки. Рассмотрим перевод следующей входной цепочки:

for i1 = c11 to c12 step c13

for i2 = c21 to c22 step c23

top

next i2

next i1,

разбор которой в виде последовательности номеров правил и синтаксического дерева приведен на рис. 4.5.

Рис. 4.5

К моменту использования правила с номером (0) на выходных лентах OUT1 и OUT2 будут находиться следующие цепочки (на данном этапе проверки считаем, что вычисление атрибутов выполняется правильно):

OUT1:

I1 := c11;

Label11: if i1 > c12 then goto Label12;

I2 := c21;

Label21: if i1 > c22 then goto Label22;

top;

OUT2:

I1 := i1 + c13; goto Label11; Label12:

I2 := i2 + c23; goto Label21; Label22:

После выполнения сцепления выходная (основная) лента будет содержать перевод:

OUT1:

I1 := c11;

Label11: if i1 > c12 then goto Label12;

I2 := c21;

Label21: if i1 > c22 then goto Label22;

top;

I1 := i1 + c13; goto Label11;

Label12:

I2 := i2 + c23;

goto Label21;

Label22:

Анализ выходной цепочки показывает, что перевод построен неверно: операторы завершения внешнего и вложенного циклов следуют в обратном порядке. Связано это с тем, что вначале на выходную ленту OUT2 записываются операторы завершения внешнего цикла и только после этого – вложенного. При формировании же перевода на выходную ленту вначале должны быть записаны операторы завершения вложенного, а затем – внешнего. Для того чтобы исправить эту ошибку, необходимо запись на ленту OUT2 и чтение из нее выполнять с одного конца, т. е. реализовать ее как стек с операциями PushOUT2 (“Втолкнуть на ленту OUT2”) и PopOUT2 (“Вытолкнуть с ленты OUT2”).

После включения операций PushOUT2 и PopOUT2 в правила (0) и (5) окончательно получим следующий вид атрибутной транслирующей грамматики:

(0) S0 S { while (OUT2 not empty) OUT1:=OUT1|| PopOUT2}

  1. S A s1 B Cn1

n1 s1

(2) A s4 for D s1, s2 Es3 Fn1, n2, n3

s4, n1 s1; n2 s2; n3 s3;

  1. D s1, s2 id a1 = cns a2

{ OUT1:= “id n1 := cns n2; Label s3: if id >” } n1, n2, n3

s1, n1 a1; n2 a2; s3 NewLabel; n3 s3; s2 n3;

  1. Es3 to cnsa1 { OUT1:= “cnsn1 then goto Labels1;” } n1, n2

n1 a1; s1 NewLabel; n2 s1; s3 n2;

  1. Fn1, n2, n3 step cnsa1

{ PushOUT2(“id := “idn6 := idn6 + cnsn7; goto Label n5; Label n4:” )}n4,n5,n6

n4 n2; n5 n3; n6 n1; n7 a1;

  1. B S

  2. B term; { OUT1:= “term; }

(8) C n1 next ida1 { if n2 n3 then Error }n2, n3

n2 n1; n3 a1

Проверка правильности вычисления атрибутов проводилась путем моделирования перевода конкретным атрибутным процессором с магазинной памятью различных тестовых входных цепочек и в пособии не рассматривается.

Соседние файлы в папке ФОРМАЛЬНЫЕ МЕТОДЫ ОПИСАНИЯ ПЕРЕВОДА