Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

spz / lect / lekc4_2_vnytr_predst

.pdf
Скачиваний:
16
Добавлен:
23.02.2016
Размер:
231.71 Кб
Скачать

Системне програмне забезпечення.. І.Яковлєва

1

Способи внутрішнього представлення програм

1.Зв'язані облікові структури, що представляють синтаксичні дерева.

2.Багатоадресний код з явно іменованим результатом (тетради).

3.Багатоадресний код з неявно іменованим результатом (тріади).

4.Обернений (постфиксна) польський запис операцій.

5.Алгоритм Дейкстри.

6.Асемблерний код або машинні команди.

7.Розбір арифметичного виразу. Алгоритм Рутисхаузера.

Можливі різні форми внутрішнього представлення синтаксичних конструкцій вихідної програми в компіляторі. На етапі синтаксичного розбору часто використовується форма, іменована деревом виведення. Але форми представлення, використовувані на етапах синтаксичного аналізу, виявляються незручними в роботі при генерації й оптимізації об'єктного коду. Тому перед оптимізацією і безпосередньо перед генерацією об'єктного коду внутрішнє представлення програми може перетворюватися в одну з відповідних форм запису.

Усі внутрішні представлення програми звичайно містять у собі дві принципово різні речі — оператори й операнди. Розходження між формами внутрішнього представлення полягають лише в тому, як оператори й операнди з'єднуються між собою. Також оператори й операнди повинні відрізнятися один від іншого, якщо вони зустрічаються в будь-якому порядку. За розрізнення операндів і операторів, як уже було сказано вище, відповідає розроблювач компілятора, що керується семантикою вхідної мови.

Відомі наступні форми внутрішнього представлення програм1 :

1.зв'язані облікові структури, що представляють синтаксичні дерева;

2.багатоадресний код з явно іменованим результатом (тетради);

3.багатоадресний код з неявно іменованим результатом (тріади);

4.обернений (постфиксна) польський запис операцій;

5.асемблерний код або машинні команди.

У кожному конкретному компіляторі може використовуватися одну з цих форм, обрану розроблювачами. Але частіше компілятор не обмежується використанням тільки однієї форми для внутрішнього представлення програми. На різних фазах компіляції можуть використовуватися різні форми, що у міру виконання проходів компілятора перетворюються одна в іншу. Не усі з перерахованих форм широко використовуються в сучасних компіляторах, і про це буде сказано по мірі їхнього розгляду.

1 Існують три форми запису виразів — префіксна, інфіксна і постфіксна. При префиксному запису операція записується перед своїми операндами, при інфіксному між операндами, а при постфіксному — після операндів. Загальноприйнятий запис арифметичних виразів являє приклад інфіксного запису. Запис математичних функцій і функцій у мовах програмування є префіксним (інші приклади префіксного запису – команди ассемблера, тріади і тетради, в тому виді, як вони розглянуті далі). Постфіксний запис у повсякденному житті зустрічається рідко. З ним зіштовхуються тільки користувачі стекових калькуляторів і програмісти мовою

Forth.

Системне програмне забезпечення.. І.Яковлєва

2

Деякі компілятори, які незначно оптимізують результуючий код, генерують об'єктний код у міру розбору вхідної програми. У цьому випадку застосовується схема СК-компіляції, коли фази синтаксичного розбору, семантичного аналізу, підготовки і генерації об'єктного коду сполучені в одному проході компілятора. Тоді внутрішнє представлення програми існує тільки умовно у вигляді послідовності кроків алгоритму розбору. У будь-якому випадку компілятор завжди буде працювати з представленням програми у формі машинних команд — інакше він не зможе побудувати результуючу програму. Далі всі перераховані форми представлення розглядаються більш детально.

Синтаксичні дерева

Синтаксичні дерева вже були розглянуті вище. Це структура, що представляє собою результат роботи синтаксичного аналізатора. Вона відображає синтаксис конструкцій вхідної мови і явно містить у собі повний взаємозв'язок операцій. Очевидно також, що синтаксичні дерева — це машинно-незалежна форма внутрішнього представлення програми.

Недолік синтаксичних дерев полягає в тому, що вони являють собою складні зв'язні структури, а тому не можуть бути тривіальним чином перетворені в лінійну послідовність команд результуючої програми. Проте вони зручні при роботі з внутрішнім представленням програми на тих етапах, коли немає необхідності безпосередньо звертатися до команд результуючої програми.

Синтаксичні дерева можуть бути перетворені в інші форми внутрішнього представлення програми, що представляють собою лінійні списки, з урахуванням семантики вхідної мови. Алгоритми такого роду перетворень розглянуті далі. Ці перетворення виконуються на основі принципів СКкомпіляції.

Багатоадресный код з явно іменованим результатом (тетради)

Тетради являють собою запис операцій у формі з чотирьох складових: операція, два операнда і результати операції. Наприклад, тетради можуть виглядати так: <операція>(<операнд1>,<операнд2>,<результат>).

Тетради являють собою лінійну послідовність команд. При обчисленні виразу, записаного у формі тетрад, вони обчислюються одна за іншою послідовно. Кожна тетрада в послідовності обчислюється так: операція, задана тетрадою, виконується над операндами і результат її виконання міститься в змінній, заданій результатом тетради. Якщо якийсь з операндів (чи оба операнда) у тетраді відсутні (наприклад, якщо тетрада являє собою унарну операцію), то він може бути опущений чи замінений порожнім операндом (у залежності від прийнятої форми запису і її реалізації).

Системне програмне забезпечення.. І.Яковлєва

3

Результат обчислення тетради ніколи опущений бути не може,

інакше

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

Тетради являють собою лінійну послідовність, а тому для них нескладно написати тривіальний алгоритм, що буде перетворювати послідовність тетрад у послідовність команд результуючої програми (або послідовність команд асемблера). У цьому їхня перевага перед синтаксичними деревами. А на відміну від команд асемблера тетради не залежать від архітектури обчислювальної системи, на яку орієнтована результуюча програма. Тому вони являють собою машинно-незалежну форму внутрішнього представлення програми.

Тетради вимагають більше пам'яті для свого представлення, чим тріади, вони також не відображають явно взаємозв'язок операцій між собою. Крім того, є складності з перетворенням тетрад у машинний код, тому що вони погано відображаються в команди асемблера і машинні коди, оскільки в наборах більшості сучасних комп'ютерів рідко зустрічаються операції з трьома операндами.

Наприклад, вираз A:=B*C+D-B*10, записане у виді тетрад, буде мати вигляд:

1.

*

(

В, С, Т1 )

2.

+

(

Т1, D, Т2 )

3.* ( С, 10,ТЗ )

4.- ( Т2,ТЗ,Т4 ) 5.:= ( Т4,0, А)

Тут всі операції позначені відповідними знаками (при цьому присвоєння також є операцією). Ідентифікатори Т1, … Т4 позначають тимчасові змінні, використовувані для збереження результатів обчислення тетрад. Варто звернути увагу, що в останій тетраді (присвоєння), що вимагає тільки одного операнда, у якості другого операнда виступає незначущий операнд «0».

Системне програмне забезпечення.. І.Яковлєва

4

Багатоадресний код з неявно іменованим результатом (тріади)

Тріади являють собою запис операцій у формі з трьох складових: операція і два операнда. Наприклад, тріади можуть мати вид: <операція>(<операнд1>,<операнд2>). Особливістю тріад є те, що один чи обидва операнда можуть бути посиланнями на іншу тріаду в тому випадку, якщо в якості операнда даної тріади виступає результат виконання іншої тріади. Тому тріади при записі послідовно нумерують для зручності вказання посилань одних тріад на інші (у реалізації компілятора як посилання можна використовувати не номера тріад, а безпосередньо посилання у виді покажчиків - тоді при зміні нумерації і порядку проходження тріад змінювати посилання не потрібно).

Тріади являють собою лінійну послідовність команд. При обчисленні виразу, записаного в формі тріад, вони обчислюються одна за іншою послідовно. Кожна тріада в послідовності обчислюється таким чином: операція, яка задана тріадою, виконується над операндами, а якщо в якості одного з операндів (або обидвох операндів) виступає посилання на іншу тріаду, то береться результат обчислення тієї тріади. Результат обчислення тріади потрібно зберігати в тимчасовій пам’яті, так як він може знадобитися наступними тріадами. Якщо якийсь з операндів в тріаді відсутній, тбт якщо тріада представляє собою унарну операцію, то він може бути опущений або замінений пустим операндом. Тріади являють собою лінійну послідовність команд, а тому для них не важко написати алгоритм перетворення послідовність тріад в послідовність команд результуючої програми або послідовність команд асемблера. В цьому їх перевага перед синтаксичними деревами. Але тут необхідний алгоритм, який би відповідав за розподілення пам’яті, яка необхідна для зберігання проміжного результату, так як тимчасові змінні для цієї цілі не використовуються. В цьому відмінність тріад від тетрад.

Так як і тетради, тріади не залежать від архітектури обчислювальної системи, на яку орієнтована результуюча програма. Тому вони являють собою машиннонезалежну форму внутрішнього представлення програми.

Тріади вимагають менше пам’яті для свого представлення, ніж тетради, вони також явно відображають взаємозалежність операцій між собою, що робить зручним їх застосування. Необхідність мати алгоритм, який відповідає за розподіл пам’яті для зберігання проміжних результатів, не є недоліком, так як зручно розподіляти результати не тільки по доступним коміркам тимчасової пам’яті, але й по регістрам процесора, які є в наявності. Це дає деякі переваги. Тріади ближчі до двухадресних машинних команд, ніж тетради, а ці команди найбільш розповсюджені в наборах команд більшості сучасних комп’ютерів.

Наприклад, вираз А:=В*C+D-B*10, записаний у вигляді тріад, буде мати вигляд:

1.*(B,C)

2.+( ^1,D)

3.*(B,10)

4.–(^2,^3)

5.:=(A,^4)

Системне програмне забезпечення.. І.Яковлєва

5

Тут операції позначені відповідним знаком, а знак ^ позначає посилання операнда однієї тріади на результат іншої.

Асемблерний код і машинні команди

Машинні команди зручні тим, що при їх використанні внутрішнє представлення програми повністю відповідає об’єктному коду і складні перетворення не потрібні. Команди асемблера являють собою тільки форму запису машинних команд, а тому в якості форми внутрішнього представлення програми практично нічим не відрізняються від них.

Але використання команд асемблера або машинних команд для внутрішнього представлення програми вимагає додаткових структур для відображення взаємозв’язку операцій. В цьому випадку внутрішнє представлення програми буде залежати від архітектури обчислювальної системи, на яку орієнтований результуючий код. Значить, при орієнтації компілятора на інший результуючий код необхідно перебудовувати як саме внутрішнє представлення програми, так і методи його обробки.

Але – машинні команди, це мова, на якому повинна бути записана результуюча програма. Тому компілятор так або інакше повинен працювати з ними. Крім того, тільки опрацьовуючи машинні команди, можна досягнути найбільш ефективної результуючої програми. Тому, довільний компілятор працює з представленням результуючої програми в формі машинних команд, але обробка виконується, як правило, на останніх етапах генерації коду.

Обернений польський запис операцій

Обернений польський запис - це постфіксний запис операцій. Вона була запропонована польським математиком Я. Лукашевичем.

В цьому записі знаки операцій записуються безпосередньо за операндами. В порівнянні із звичайним, інфіксним записом операцій в польському записі операнди слідують в тому ж порядку, а знаки операцій – строго в порядку їх виконання. Це робить цей запис дуже зручним для обчислення виразів на компьютері. Польський запис не вимагає враховувати пріоритет операцій, в ній не використовуються дужки, і в цьому її основна перевага.

Він надзвичайно ефективний, коли для обчислення використовується стек.

Головний недолік – так як використовується стек, то для роботи з ним завжди доступна тільки його верхівка, а це робить дуже важкою оптимізацію виразів. Практично вирази в формі оберненого польського запису не піддаються оптимізації.

Але вона широко використовується для обчислення виразів в інтерпретаторах і командних процесорах, де оптимізація обчислень або відсутня зовсім, або не ає суттєвого значення.

Системне програмне забезпечення.. І.Яковлєва

6

Обчислення виразу 6+7*(10+4)=104

 

6

7 10 4 +

* +

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Нехай заданий аpифметичний виpаз виду:

(A+B)*(C+D)-E (1)

Пpедставим цей виpаз у вигляді деpева, в якому вузлам відповідають опеpації, а віткам - опеpанди. Побудову почнемо з коpеня, у якості якого вибиpається опеpація, що виконується останньою. Лівій вітці відповідає лівий опеpанд опеpації, а пpавій - пpавий. Деpево виpазу

(1) показане на pис. 1.

-

/ \ / \

*E

/\

/\

/\

/\

++

/

/ \

/

/ \

\

\

A

B

C

D

 

pис.

1

Здійсним обхід деpева, під яким будемо розуміти фоpмування рядка символів із символів вузлів і віток деpева. Обхід будемо здійснювати від самої лівої вітки впpаво й вузол пеpеписувати у вихідний рядок тільки після pарозгляду всіх його віток. Результат обходу деpева має вигляд:

AB+CD+*E- (2)

Особливістю виразу (2) є в тому, що символи опеpацій містяться за символами опеpандів і у відсутності дужок. Такий запис називається оберненим польським записом.

Обернений польський запис володіє низкою властивостей, які перетворюють його в ідеальну пpоміжну мову пpи тpансляції. По-пеpших, обчислення виpазу, записаного в оберненому польському записі, може пpоводиться шляхом однокpатного пpосмотpа, що є досить зручним пpи генеpации об'єктного коду пpогpам. Наприклад, обчислення виpазу (2) може бути виконано наступним чином:

|

-----#

|----------------------

Анализиpуемая

|-----------------------

Дія

|

|

|

|

|

|

| п/п |

 

стpока

|

 

 

|-----

0

|----------------------

A B + C D + * E -

|-----------------------

r1=A+B

 

|

|

|

|

 

|

|

1

|

r1

C D + * E -

|

r2=C+D

 

|

|

2

|

r1

r2 * E -

|

r1=r1*r2

 

|

|

3

|

r1

E -

|

r1=r1-E

 

|

|

4

|

r1

 

|

Обчислення кінчене

|

|-----

 

|----------------------

 

 

|-----------------------

 

 

|

Системне програмне забезпечення.. І.Яковлєва

7

Отут r1, r2 - допоміжні пеpеменные.

По-друге, отримання оберненого польського запису з вхідного виразу може здійснюватися досить пpосто на основі алгоpитму, запропонованого Дейкстpой. Для цього вводитися поняття стекового пpіоpитета опеpаций(табл. 1):

Таблиця 1 |----------|-----------| | Опеpація | Пpіоpитет | |----------|-----------|

|

(

|

0

|

|

)

|

1

|

|

+|-

|

2

|

|

*|/

|

3

|

|

**

|

4

|

|----------|-----------|

Пpоглядається вхідний рядок символів зліва напpаво, опеpанди пеpеписуються у вихідний рядок, а знаки опеpацій заносяться в стек на основі наступного:

а) якщо стек порожній, то опеpація з вхідного рядка пеpеписується в стек;

б) опеpація виштовхує зі стека всі опеpації з більшим або pівним пpіоpитетом у вихідний рядок; в) якщо черговий символ з вхідного рядка є ліва дужка „(”, то він проштовхується в стек;

г) права дужка „”) виштовхує всі опеpації зі стека до найближчої лівої дужки, самі дужки у вихідний рядок не пеpеписуються, а знищують одна одну.

Пpоцес одержання оберненого польського запису виpазу (1) схематично пpедставлений на pис. 2:

символ

1

2

3

4

5

6

7

8

 

9

10

11

12

13

 

який переглядаємо

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Вхідний рядок

(

A

+

B

)

*

(

C

+

D

)

-

E

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Стан

 

 

+

+

 

 

(

(

 

+

+

 

 

 

 

стека

(

(

(

(

 

*

*

*

 

(

(

*

-

-

 

 

 

 

 

 

 

 

 

 

 

*

*

 

 

 

 

Вихідний

 

A

 

B

+

 

 

C

 

 

D

+

*

E

-

 

 

 

 

 

 

рядок

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Рис. 2

 

 

 

 

Розбір арифметичного виразу

Алгоритм Рутисхаузера

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

D:=((C-(B*L))+K)

Обробляючи вираз з повною скобковою структурою, алгоритм привласнює кожному символу з рядка номер рівня по наступних пpавилах:

1.якщо це ліва дужка „(” або змінна, то значення збільшується на 1;

2.якщо знак опеpації або права дужка „)”, то зменшується на 1.

Системне програмне забезпечення.. І.Яковлєва

8

Для виразу (А+(В+С)) присвоювання значень рівня буде відбуватися таким способом :

|----------

|------------------------------------

 

 

 

 

 

 

 

 

|

|N симв.| 1

2

3

4

5

6

7

8

9

|

|----------

|------------------------------------

 

 

 

 

 

 

 

 

|

|символ|

 

 

 

 

 

 

 

 

|

|

| ( A + ( B * C ) ) |

|----------

|------------------------------------

 

 

 

 

 

 

 

 

|

|номер |

 

 

 

 

 

 

|

 

 

|рівня

| 1

2

1

2

3

2

3

2

1

|

|----------

|------------------------------------

 

 

 

 

 

 

 

 

|

Алгоритм складається з наступних кроків:

1.виконати розміщення рівнів(пріоритетів);

2.виконати пошук елементів рядка з максимальним значенням рівня;

3.виділити трійку - два операнда з максимальним значенням рівня й операцію, що лежить між ними;

4.результат обчислення трійки позначити допоміжною змінною;

5.з вхідного рядка видалити виділену трійку разом з її дужками, а на її місце помістити допоміжну змінну, що позначає результат, зі значенням рівня на одиницю менше, ніж у виділеної трійки;

6.виконувати п.п.2 - 5 доти, поки у вхідному рядку не залишиться одна змінна, що позначає загальний результат виразу.

Приклад розбору :

|---------------

|-------------------------------------------

 

|

|Геновані

|

Вираз

|

| тріади

|

 

|

|---------------

|-------------------------------------------

 

|

|

| ( ( ( ( А + В ) * С ) / D ) - E )

|

|

|0 1 2 3 4 5 4 5 4 3 4 3 2 3 2 1 2 1 0|

|

|

 

|

|+ А В - > R |

( ( ( R * C ) / D ) - E )

|

|

| 0 1 2 3 4 3 4 3 2 3 2 1 2 1 0

|

|

|

 

|

|* R C -> S |

( ( S / D ) - E )

|

|

|

0 1 2 3 2 3 2 1 2 1 0

|

Системне програмне забезпечення.. І.Яковлєва

9

|

 

 

 

| / S D -> Q |

( Q - E )

|

|

|

0 1 2 1 2 1 0

|

|

|

 

|

|- Q E -> T

|

T

|

 

|

 

|

|---------------

|--------------------------------------------

 

|

Соседние файлы в папке lect