Міністерство освіти і наукиУкраїни
Національнийуніверситет “Львівська політехніка”/>КУРСОВА РОБОТА
Здисципліни: «Системне програмування»
натему:
“Розробкакомпілятора з вхідної мови програмування”
Виконав:студ. гр. КІМ-21з
МевшукГ.Г.
Львів2011
/>Анотація
Згідно заданого завдання в даномукурсовому проекті розроблено компілятор з вхідної мови програмування Pascal.Оболонка компілятора розроблена в середовищі програмування Borland C підопераційну систему Windows і в опис проекту не входить. Сам компіляторнаписанний на мові Pascal, та поданий у пояснювальній записці, а також разом зоболонкою в електронному варіанті. В пояснювальній записці подано детальнийопис мови, огляд існуючих методів розробки компіляторів, а також описано процесрозробки програми компілятора на рівні блок-схем і тексту програми. До проекту доданорезультати тестування програми.
Зміст
Вступ
1. Завдання на курсовий проект
2. Формальний опис вхідної мови програмування
3. Розробка компілятора вхідної мови програмування
3.1 Розробка лексичного аналізатора
3.1.1 Розробка блок-схеми програми
3.2 Розробка синтаксичного аналізатора
3.2.1 Обробка синтаксичних помилок
3.3 Розробка семантичного аналізатора
3.4 Розробка оптимізатора коду
3.5 Розробка генератора коду
4. Відладка та тестування компілятора
4.6.1 Виявлення лексичних помилок
4.6.2 Виявлення синтаксичних помилок
4.6.3 Виявлення семантичних помилок
4.6.4 Загальна перевірка коректності роботитранслятора
Висновки
Література
Додатки
Вступ
Компілятор – це програма, яка читаєтекст програми, написаної на одній мові – початковій, і транслює (переводить)його в еквівалентний текст на іншій мові – цільовій. Одним з важливих моментівтрансляції є повідомлення користувача про наявність помилок в початковійпрограмі.
Створення компіляторів є одною зневід‘ємних частин системного програмного забезпечення. Одним із завданькомпілятора є переведення написаного тексту програми у машинний код, якийповинен відповідати комп‘ютерній системі. Оскільки сьогоднішній час – часвеликого розвитку комп‘ютерної галузі, то створений машинний код з часом стаєзастарілим, тобто не відповідає принципу оптимального використання комп‘ютернихресурсів. Тому для запобігання цього явища необхідно створювати новікомпілятори, які б відповідали потребам теперішнього часу.
Проблема компіляції полягає в пошукувідповідності тексту вхідної програми конструкціям, що визначені граматикою.Граматика визначає форму або синтаксис допустимих виразів мови. Тому текствхідної мови зручно подавати у вигляді послідовності лексем, що є неподільнимиодиницями мови. За допомогою компілятора програміст повинен мати можливістьредагувати текст вхідної мови. Для цього компілятор має виявляти всіневідповідності тексту програми конструкціям мови і у випадку відсутностіпомилок генерувати об'єктний код або виконавчий модуль.
На перший погляд, різноманітністькомпіляторів приголомшує. Використовуються тисячі початкових мов, відтрадиційних, таких як Fortran і Pascal, до спеціалізованих, виникаючих у всіхобластях комп'ютерних технологій. Цільові мови не менше різноманітні – цеможуть бути інші мови програмування, різні машинні мови – від мов мікропроцесорівдо суперкомп'ютерів. Іноді компілятори класифікують як однопрохідні,багатопрохідні, виконуючі, відлагоджуючі, оптимізуючі — залежно від призначенняі принципів та технологій їх створення. Не дивлячись на уявну складність ірізноманітність, основні задачі, виконувані різними компіляторами, по суті,одні і ті ж. Розуміючи ці задачі, ми можемо створювати компілятори для різнихпочаткових мов і цільових машин з використанням одних і тих же базовихтехнологій. Знання про організацію і написання компіляторів істотно зросли зчасів перших компіляторів, що з'явилися на початку 1950-х рр. Сьогодні складновизначити, коли саме з'явився на світ перший компілятор, оскільки в ті рокипроводилося багато експериментів і розробок різними незалежними групами. Восновному метою цих розробок було перетворення в машинний код арифметичнихформул. В 50-х роках про компілятори ходила слава як про програми, украйскладні в написанні (наприклад, на написання першого компілятора Fortran буловитрачено людиною 18 — років роботи). З тих пір розроблені різноманітнісистемні технології рішення багатьох задач, що виникають при компіляції. Крімтого, розроблені хороші мови реалізації, програмні середовища і програмнийінструментарій. Проблема компіляції полягає в пошуку відповідності текстувхідної програми конструкціям, що визначені граматикою. Граматика визначаєформу або синтаксис допустимих виразів мови… Тому текст вхідної мови зручноподавати у вигляді послідовності лексем, що є неподільними одиницями мови. Задопомогою компілятора програміст повинен мати можливість редагувати текствхідної мови. Для цього компілятор має виявляти всі невідповідності текступрограми конструкціям мови і у випадку відсутності помилок генерувати об'єктнийкод або виконавчий модуль. Сьогодні від компілятора вимагається також створенняоптимізованого коду програми. Тому створення ефективного виконавчого коду єдуже проблемою в наш час, бо для створення цього необхідно враховувати всіможливі варіанти покращеної апаратної частини, розвиток якої досяг сьогоднітеоретичної межі продуктивності.
Завдяки цьому «соліднийкомпілятор» може бути реалізований навіть як курсова робота попроектуванню компіляторів.
1. Завдання на курсовий проект
Розробитикомпілятор з вхідної мови програмування короткий опис якої подано нижче (увідповідності з заданим варіантом) з виводом необхідної проміжної інформації накожному кроці. Розробити інтерфейс користувача (інтегроване середовищепрограмування вхідною мовою).
Варіант № 13
В таблиціваріанта завдання використано наступні позначення:
A: Тип даних:byte(1).
B: Додатковаарифметична операція: ^ (піднесення до степеня).
C: Додатковалогічна операція: NOT.
D: Операторциклу: do-while (2).№ A B C D 13 1 (byte) ^ NOT do-while
2. Формальний опис вхідноїмови програмування
Розробитикомпілятор заданої вхідної мови програмування.
- три типиданих: логічний тип даних (boolean), знаковий цілочисельний тип (1byte) табеззнаковий цілочисельний тип розміром 1 байт;
- змінних здовільної довжини;
- арифметичніоперації над цілими: +, -, *, /, “-” (унарний мінус), ^ (операція піднесення достепеню);
- символигрупування арифметичних операцій “(”, “)”
- логічніоперації над цілими: , ==, | = ;
- логічнуоперацію над логічними даними NOT;
- операторприсвоєння “=”;
- операториблоку “{“, “}”;
- операторвиводу (print);
- операторвиконання дії за умовою (if-then-else);
- операторциклу (do-while);
Визначимо окремітермінальні символи та нерозривні набори термін.
Символів(ключовіслова);
{( usigned
} ) char
;
= > then
+ = = else
— while
^ NOT do
* program true
/ bool false
print
До термінальнихсимволів віднесемо також усі цифри (0-9), латинські букви (a-z, A-Z) та символпробілу (“ ”). Всього 28+10+52+1=91 термінальних виразів. Це “цеглинки”, з якихмає будуватися текст будь-якої вхідної програми.
- символ “| ” розділяє альтернативи
- символи “[ ”,“ ] ” означає необов’язковість
- символи “{ ”,“ } ” означає повтор
Формальний описзаданої мови BNF:
::=prog
::={ | [{}]}
::= |
::=;
::=| | |
::== ;
::=if then [else]
::=while do
::=print | | ;
::=boolean | byte | ubyte
::=[{}]
::=[] [{}]
::=true | false
::=a | …| z | A | … | Z
::=0 | 1 | … | 9
::=true | fals
::=||
::=[{}]
::=[{}]
::=[{|}]
::=() | |
::=()| |
::=* | / | + | — | ^
::= | = = |
::=[{NOT}]
::() | |
/>/>/>
3. Розробка компіляторавхідної мови програмування
Процедурно-орієнтовані мовипрограмування, такі як FORTRAN, Pascal, C, C++ та ін. засновані на принципіпослідовного виконання операторів, команд або директив. Їх базові оператори,операції, команди та директиви можна класифікувати по трьох основних групах:
а) Безумовніоператори (statements) обрахунків та перетворень;
б) Операториобробки розгалужень та передачі управління;
в) Операториорганізації циклічної обробки.
В загальному випадку віртуальнамашина складається з інформаційного, операційного, управляючого такомунікаційного компонентів. Будь-яка віртуальна машина повинна мати:
а) Пам’ятьрізних видів та типів для збереження кодів програм і даних (інформаційнийкомпонент) і механізми доступу до неї;
б) Вказівникпоточної операції (основа управляючої компоненти), що змінюється при підрахункуномера оператора;
в) Блоквиконання операцій (operators), який реалізує функції операційних тауправляючих компонентів. Разом з інтерфейсним обладнанням він реалізуєкомунікаційний компонент, який забезпечує зв’язок ВМ із зовнішнім світом.
Загальна схема компілятора
Можна виділити сім різних логічнихзадач:
а. Інтерпретація– визначення точного змістового навантаження, створення матриці та таблиць здопомогою програм інтерпретації;
б. Машинно-незалежнаоптимізація – створення оптимальнішої матриці;
в. Лексичнийаналіз – розпізнавання базових елементів та створення стандартних символів;
г. Синтаксичнийаналіз – розпізнавання базових синтаксичних конструкцій з використаннямредукцій;
д. Розподілпам’яті – модифікація таблиць ідентифікаторів та літералів. В матрицірозміщується інформація, з допомогою якої генерується код, який розподіляєдинамічну пам’ять;
е. Генераціякоду – використання макропроцесора для отримання більш оптимального вихідногокоду;
Фази з першої по четвертумашинно-незалежні і визначаються тільки мовою. Фази з п’ятої по сьому –машинно-залежні і не залежать від мови. З метою забезпечення вищої ефективностіці фази можуть не розділятись так чітко.
Компілятор необхіднооцінювати не тільки по об’єктному коду, що генерується, але також і по об’ємуоперативної пам’яті, що він займає і по часу, що потрібен для трансляції. Нажаль, ці критерії оптимальності часто досить суперечливі. Крім того,оптимальність коду обернено пропорційна складності, розміру та часу роботисамого компілятора. Насправді необхідні компроміси.
Компілятором використовуютьсябази даних, що забезпечують зв’язки між фазами: вхідний код, таблицястандартних символів, таблиця термінальних символів, таблиця ідентифікаторів,таблиця літералів, редукції, матриця, кодові продукції, код компоновки,кінцевий об’єктний код.
3.1 Розробкалексичного аналізатора
Для обробки текстів вхідних програміснує кілька способів побудови мовних процесорів. Одним з варіантів єінтерпретатор.
Інтерпретатор – це програма, якаобробляє вхідну програму, написану на вхідній мові і проводить обчислення,задані цією мовою.
Транслятор – програма, щообробляє вхідну програму, написану на вхідній мові і генерує програму наоб’єктній мові. Об’єктна мова як правило є мовою деякої ЕОМ і таку програмуодразу можна виконувати. Транслятори можна поділити на асемблери і компілятори,які транслюють відповідно мови низького і високого рівня.
Перед написанням компілятора вхіднумову потрібно подати в термінах деякої граматики. Ця граматика визначає формуабо синтаксис допустимих виразів мови. Проблема компіляції – проблема пошуку втексті вхідної програми конструкцій, що визначені граматикою та генераціївідповідного коду для кожного виразу вхідної мови. Конструкції вхідної мовизручно подавати у вигляді лексем. Лексеми – це неділимі фундаментальні одиницімови. Перегляд вхідного тексту та розпізнавання лексем називається лексичниманалізом, а процедура, яка його виконує – сканером. Після розпізнавання лексемкожен вираз можна розпізнати як окрему конструкцію мови, що описані граматикою.Цей процес називається синтаксичним аналізом.
Процес розробки компілятораможна зобразити такою схемою:
/>
Рисунок 1 — Процес розробкикомпілятора.
Сканер або лексичний блок –одна з найпростіших частин компілятора. Він переглядає символи вхідної програмизліва направо і групує певні термінальні символи в єдині синтаксичні об’єкти.Цілі числа, ідентифікатори, символьні константи є нетермінальними символами ітакож групуються в таблиці.
Процес роботи лексичного аналізаторає складнішим. Побудований при лексичному аналізі список лексем проглядаєтьсязліва направо і аналізується. Є два основні способи для аналізу списку лексем.Це спосіб операторного передування і рекурсивний спуск.
Для роботи компілятора різнадопоміжна інформація знаходиться в таблицях, якими дані передаються від одногодо іншого етапу. В деяких реалізаціях записи, що заносяться в таблицю можутьмати різну довжину. Таким чином компілятору потрібні методи організаціїінформації, здатні швидко запам’ятати і видати інформацію про велику частинупрограми. Основну проблему синтаксичного аналізу можна сформулювати так: євелика кількість об’єктів, що можуть зустрічатися в програмі. Об'єкти можутьзустрічатися в непередбачуваному порядку. Як тільки об’єкт зустрівся, потрібноперевірити, чи не був він раніше занесений в таблицю. Якщо в таблиці об’єктунемає, його необхідно туди записати. Одним із способів запам'ятовуванняоб'єктів є таблиці з прямим доступом.
При рекурсивному спуску відповідністьблоків лексем синтаксичним правилам перевіряється відповідно до дерева розборудеякої конструкції.
Покажемо на прикладі деревоконструкції while мови С.
While (f+a*d) a=a+5;
/>
Рисунок 2 Конструкції while мови С
При знаходженні ключового слова whileперевіряється наявність дужки і викликається процедура перевірки запису виразу.
При перегляді виразу методомоператорного передування лексеми проглядаються в обох напрямках, якщо цепотрібно і визначається, чи порядок їх співпадає з описаними конструкціями.
На етапі синтаксичного аналізувизначаються помилки. Після того, як синтаксис оператора визначено, ідеінтерпретація його значення, тобто семантичний аналіз. Кожній синтаксичнійодиниці відповідає певне значення, яке може бути виражене в формі реальнихвходів або в проміжній формі. Проміжною формою синтаксичної конструкції єдерево розбору.
Після синтаксичного і семантичногоаналізу тексту програми відбувається трансляція тексту в проміжну мову, якою убільшості випадків є асемблер. Трансляція тексту може відбуватись як за один,так і за декілька проходів. Перевагою однопрохідних компіляторів є те, що непотрібно підтримувати зв’язок між проходами, але недоліком є те, що всі змінні,функції, мітки і константи мусять обов’язково бути описані перед їхвикористанням./>/>/>Вибірструктур вхідних даних
Процес компіляції вхідного файлуможна розділити умовно на кілька етапів. Основні етапи – це лексичний,синтаксичний та семантичний аналіз та трансляція. На кожному з цих етапів даніпредставляються у зручному для обробки вигляді.
Для лексичного аналізу вхіднимиданими є текстовий файл. Лексичний аналізатор формує динамічний список восновній пам'яті, в якому кожен вузол містить як інформацію про лексему, так івказівник на наступну лексему. Останній вказівник має значення NULL. Структуравузла списку:
NODE
{ int line;– номер рядка лексеми
int idlexem;– код лексеми (номер утаблиці)
int type;– тип, якщо це ідентифікатор
int is_int;– чи це ідентифікатор, чирядкова константа, чи число
int is_ar;– кількість індексів масиву(для простої змінної – 0)
NODE* next; }– вказівник на наступнийелемент списку
Кожен ідентифікатор, не знайдений втаблиці лексем, записується в таблицю лексем, що є двовимірним символьниммасивом. Паралельно існує масив вказівників на список параметрів, в якому всіелементи, що відповідають функції, мають вказівник на список параметрівфункції. Структура вузла цього списку:
FPAR {
int type;– тип параметра (1..4)
FPAR* next; }– вказівник на наступнийелемент списку
Список лексем є вхідним длясинтаксичного аналізатора, який є суміщений з семантичним. На виходісинтаксичного аналізатора є інформація про аналіз, що записується у файл. Це єповідомлення про першу знайдену помилку або ж повідомлення про те, що помилокнемає.
Якщо помилки немає, запускаєтьсятранслятор, який транслює список лексем у файл на проміжній мові С++ відповіднодо таблиці С-лексем і правил побудови вхідної мови.
Порміжний файл з розширенням странслюється у ехе-файл стандартним компілятором bсс.ехе.3.1.1 Розробкаблок-схеми програми
Для побудови компіляторанеобхідно спроектувати його будову на рівні функцій і їх взаємозв'язків, тобтоправил виклику. Це потрібно для побудови узгодженої багатомодульної структурипри програмуванні зверху вниз. Кожну задачу розділяємо на дрібніші підзадачі,які потім в свою чергу уконкретнюємо.
Загальну структуру програми можнаподати в такому спрощеному вигляді:
/>
Рисунок 3 Загальна структура програмиу спрощеному вигляді
/>/>/>/>3.2 Розробка синтаксичного аналізатора
Синтаксичний аналіз – це процес, вякому досліджується послідовність лексем, яку повернув лексичний аналізатор, івизначається, чи відповідає вона структурним умовам, що сформульовані увизначені синтаксису мови.
Синтаксичний аналізатор – це частинакомпілятора, яка відповідає за виявлення основних синтаксичних конструкційвхідної мови. В задачу синтаксичного аналізатора входить: знайти та виділитиосновні синтаксичні конструкції мови в послідовності лексем програми,встановити тип та правильність побудови кожної синтаксичної конструкції іпредставити синтаксичні конструкції у вигляді, зручному для подальшої генераціїтексту результуючої програми. Крім того, важливою функцією є локалізаціясинтаксичних помилок. Як правило, синтаксичні конструкції мови програмуванняможуть бути описані за допомогою контекстно-вільних граматик. Розпізнавач даєвідповідь на питання, належить чи ні, ланцюжок вхідних лексем заданій мові. Алезадача синтаксичного аналізу не обмежується тільки такою перевіркою.Синтаксичний аналізатор повинен мати деяку результуючу мову, за допомогою якоївін передає наступним фазам компіляції інформацію про знайдені і розібранісинтаксичні конструкції, якщо відокремлюється фаза генерації об’єктного коду.
Кожна мова програмування має правила,які визначають синтаксичну структуру коректних програм. В Pascal, наприклад,програма створюється з блоків, блок – з інструкцій, інструкції – з виразів,вирази – з токенів і т.д. Синтаксис конструкцій мови програмування може бутиописаний за допомогою контекстно-вільних граматик або нотації БНФ (Backus-NaurForm, форма Бекуса-Наура). Граматики забезпечують значні переваги розробникаммов програмування і авторам компіляторів.
Граматика дає точну і при цьомупросту для розуміння синтаксичну специфікацію мови програмування.
Для деяких класів граматик можливоавтоматично побудувати ефективний синтаксичний аналізатор, який визначає, чикоректна структура початкової програми. Додатковою перевагою автоматичногостворення аналізатора є можливість виявлення синтаксичних неоднозначностей таінших складних для розпізнавання конструкцій мови, які інакше могли бзалишитися непоміченими на початкових фазах створення мови і його компілятора.
Правильно побудована граматика додаємові програмування структуру, яка сприяє полегшенню трансляції початковоїпрограми в об'єктний код і виявленню помилок. Для перетворення описівтрансляції, заснованих на граматиці мови, в робочій програмі є відповіднийпрограмний інструментарій.
З часом мови еволюціонують,збагатившись новими конструкціями і виконуючи нові задачі. Додаванняконструкцій в мову виявиться більш простою задачею, якщо існуюча реалізаціямови заснована на його граматичному описі.
Є три основні типи синтаксичниханалізаторів граматик. Універсальні методи розбору, такі як алгоритмиКока-Янгера-Касамі або Ерлі, можуть працювати з будь-якою граматикою. Проте ціметоди дуже неефективні для використання в промислових компіляторах. Методи,які звичайно використовуються в компіляторах, класифікуються як низхідні(зверху вниз, top-down) або висхідні (з низу до верху, bottom-up). Як видно зназв, низхідні синтаксичні аналізатори будують дерево розбору зверху вниз (долистя), тоді як висхідні починають з листя і йдуть до кореня. В обох випадкахвхідний потік синтаксичного аналізатора сканується посимвольно зліва направо.Найефективніші низхідні і висхідні методи працюють тільки з підкласами граматик,проте деякі з цих підкласів, такі як LL- і LR-граматики, достатньо виразні дляопису більшості синтаксичних конструкцій мов програмування. Реалізовані вручнусинтаксичні аналізатори частіше працюють з LL-граматиками. Синтаксичніаналізатори для дещо більшого класу LR-граматик звичайно створюються задопомогою автоматизованих інструментів. Будемо вважати, що вихід синтаксичногоаналізатора є деяким представленням дерева розбору вхідного потоку токенів,виданого лексичним аналізатором. На практиці є безліч задач, які можутьсупроводжувати процес розбору, — наприклад, збір інформації про різні токени втаблиці символів, виконання перевірки типів і інших видів семантичного аналізу,а також створення проміжного коду. Всі ці задачі представлені одним блоком "Іншізадачі початкової фази компіляції" ( рисунок 4).
/>
Рисунок 4 Місце синтаксичногоаналізатора в моделі компілятора
3.2.1 Обробка синтаксичнихпомилок
Якщо компілятор матиме справу виключноз коректними програмами, його розробка і реалізація істотно спрощуються. Протедуже часто програмісти пишуть програми з помилками, і добрий компілятор повинендопомогти програмісту знайти їх і локалізувати. Примітно, що хоча помилки —явище надзвичайно поширене, лише в декількох мовах питання обробки помилокрозглядалося ще на фазі створення мови. Наша цивілізація істотно відрізнялася бвід свого нинішнього стану, якби в природних мовах були такі ж вимоги досинтаксичної точності, як і в мовах програмування.
Більшість специфікацій мовпрограмування, проте, не визначає реакції компілятора на помилки — це питаннявіддається на відкуп розробникам компілятора. Проте планування системи обробкипомилок з самого початку роботи над компілятором може як спростити йогоструктуру, так і поліпшити його реакцію на помилки.
Будь-яка програма потенційно міститьбезліч помилок самого різного рівня. Наприклад, помилки можуть бути
• лексичними, такими як невірнозаписані ідентифікатори, ключові слова або оператори;
• синтаксичними, наприклад,арифметичні вирази з незбалансованими дужками;
• семантичними, такими як оператори,використані до несумісних операндів;
• логічними, наприклад нескінченнарекурсія.
Часто основні дії по виявленнюпомилок і відновленню після них концентруються у фазі синтаксичного аналізу.Одна з причин цього полягає в тому, що багато помилок за своєю природою єсинтаксичними або виявляються, коли потік токенів, що йде від лексичногоаналізатора, порушує визначаючі мова програмування граматичні правила. Другапричина полягає в точності сучасних методів розбору; вони дуже ефективновиявляють синтаксичні помилки в програмі. Визначення присутності в програмісемантичних або логічних помилок — задача набагато складніша.
Обробник помилок синтаксичногоаналізатора має просту формульовану мету:
• він повинен ясно і точноповідомляти про наявність помилок;
• він повинен забезпечувати швидкевідновлення після помилки, щоб продовжити пошук подальших помилок;
• він не повинен істотноуповільнювати обробку коректної програми.
Ефективна реалізація цієї мети євельми складною задачею. На щастя, звичайні помилки достатньо прості, і для їхобробки часто достатньо простих механізмів обробки помилок.
В деяких випадках, проте, помилкаможе відбутися задовго до моменту її виявлення (і за багато рядків коду домісця її виявлення), і визначити її природу вельми непросто. В складнихситуаціях обробник помилок, по суті, повинен просто здогадатися, що саме мав наувазі програміст, коли писав програму. Деякі методи розбору, такі як LL і LR,виявляють помилки, як тільки це стає можливим. Точніше кажучи, вони володіютьвластивістю перевірки коректності префіксів, тобто виявляють помилку, як тількиз'ясовується що префікс вхідної інформації не є префіксом жодного коректногорядка мови.
/>3.3 Розробка семантичного аналізатора
В процесі роботи компілятор зберігаєінформацію про об'єкти програми. Як правило, інформація про кожний об'єктскладається із двох основних елементів: імені об'єкта і його властивостей.Інформація про об'єкти програми повинна бути організована так, щоб пошук її бувпо можливості швидше, а необхідної пам'яті по можливості менше. Крім того, збоку мови програмування можуть бути додаткові вимоги.
Імена можуть мати певну областьвидимості. Наприклад поле запису повинне бути унікально в межах структури (аборівня структури), але може співпадати з ім'ям об'єктів зовні запису (або іншогорівня запису). В той же час ім'я поля може відкриватися оператором приєднання, ітоді може виникнути конфлікт імен (або неоднозначність в трактуванні імені).Якщо мова має блокову структуру, то необхідно забезпечити такий спосібзберігання інформації, щоб, по-перше підтримувати блоковий механізм видимості, апо-друге – ефективно звільняти пам'ять по виході з блоку. В деяких мовах(наприклад, Аді) одночасно (в одному блоці) можуть бути видимі декілька об'єктівз одним ім'ям, в інших така ситуація неприпустима. Є декілька основних способіворганізації інформації в компіляторах:
· таблиціідентифікаторів;
· таблиці символів;
· способиреалізації блокової структури;
Перевірка типів
Компілятор повинен переконатися, щопочаткова програма слідує як синтаксичним, так і семантичним угодам початковоїмови. Така перевірка, іменована статичній (static checking), — на відміну віддинамічної, виконуваної в процесі роботи цільової програми, — гарантує, щобудуть виявлені певні типи програмних помилок.
Нижче представлені приклади статичнихперевірок.
1. Перевірки типів. Компіляторповинен повідомляти про помилку, якщо оператор застосовується до несумісному зним операнда, наприклад при складанні змінних, що є масивом і функцією.
2. Перевірки управління. Передачауправління за межі мовних конструкцій повинна проводитися в певне місце.Наприклад, в мові програмування С оператор break передає управління за межісамої вкладеної інструкції while, for або switch; якщо ж такі відсутні, товиводиться повідомлення про помилку.
3. Перевірки єдиності. Існуютьситуації, коли об'єкт може бути визначений тільки один раз. Наприклад, в мовіпрограмування Pascal ідентифікатор повинен оголошуватися тільки один раз, всімітки в конструкції case повинні бути різний, а елементи в скалярному типі неповинні повторюватися.
4. Перевірки, пов'язані зіменами. Іноді одне і те ж ім'я повинне використовуватися двічі (або більшечисло раз). Наприклад, в мові програмування Ada цикл або блок може мати ім'я,яке повинне знаходитися як на початку, так і в кінці конструкції.
Компілятор повинен перевірити, що вобох місцях використовується одне і те ж ім'я. В цьому розділі нас, в першучергу, цікавить перевірка типів. Як видно з наведених приклади, більшість іншихстатичних перевірок є рутинною і може бути реалізований з використаннямтехнологій, описаних в попередньому розділі. Деякі з них можуть використовуватисяі для виконання інших дій. Наприклад, при внесенні інформації про ім'я втаблицю символів ми можемо переконатися в єдиності оголошення даного імені.Багато компіляторів Pascal об'єднують статичні перевірки і генерацію проміжногокоду з синтаксичним аналізом. За наявності складніших конструкцій, на зразоктих, що використовуються в мові програмування Ada, може виявитися більш зручнимвиконати окремий прохід для проведення перевірок типів між синтаксичниманалізом і генерацією проміжного коду, як показано на рисунку 5.
/>
Рисунок 5 Місце семантичногоаналізатора в моделі компілятора.
Програма перевірки типів перевіряє,щоб тип конструкції відповідав очікуваному в даному контексті. Наприклад,вбудований арифметичний оператор mod в Pascal вимагає цілих операндів, томупрограма перевірки типів повинна перевірити, щоб операнди mod в початковійпрограмі — цілого типу. Так само програма перевірки типів повинна переконатися,що операція розіменування застосовується до покажчика, індексація виконується змасивом, що визначена користувачем функція./> 3.4 Розробка оптимізатора коду
компілятор програмування оболонкаопераційнаОптимізація програмногокоду — це модифікація програм, виконувана оптимізуючим компілятором або інтерпретаторомз метою поліпшення їхніх характеристик, таких як продуктивності абокомпактності, — без зміни функціональності.Оптимізація — необов'язковий, але важливий етап компіляції. У принципі, вона може відбуватисяпід час трансляції програми, але, як правило, оптимізацію програми виділяють якокремий етап функціонування компіляторів. Компонувальники також можутьвиконувати частина оптимізацій, таких як видалення невикористовуванихпідпрограм або перевпорядкування підпрограм.Розрізняють низько- івисокорівневу оптимізацію. Низкорівнева оптимізація перетворює програму нарівні елементарних команд, наприклад, інструкцій процесорів архітектури x86. Високорівневаоптимізація здійснюється на рівні структурних елементів програми, таких якрозгалуження й цикли.3.5 Розробка генератора коду
Останньою стадією розробкикомпілятора є генератор коду, який дістає на вхід проміжне представленнявихідної програми і виводить еквівалентну цільову програму.
Традиційно, до генератора кодувисуваються жорсткі вимоги. Вихідний код повинен бути коректним івисокоякісним, що означає ефективне використання ресурсів цільової машини. Крімтого, ефективно повинен бути розроблений і сам генератор коду.
Математично, проблема генераціїоптимального коду є нерозв’язною. На практиці ми вимушені користуватисьевристичними технологіями, які дають хороший, але не обов’язково оптимальнийкод. Вибір евристики дуже важливий, оскільки детально розроблений алгоритмрозробки генератора коду може давати код, що працю в декілька раз швидше, ніжкод отриманий недостатньо продуманим алгоритмом.
Хоча дрібні деталі генератора колузалежать від цільової машини і операційної системи, такі питання, як керуванняпам’яттю, вибір інструкцій, розподіл регістрів і порядок обчислень, властивіусім задачам, зв’язаним з генерацією коду.
Вхідний потік генератора коду являєсобою проміжне представлення вихідної програми, отримане на початковій стадіїкомпіляції, разом із таблицею символів, яка використовується для обчисленняадрес часу виконання об’єктів даних, зазначених в проміжному представленнііменами.
Результатом генератора коду являєтьсяцільова програма. Подібно до проміжного коду, результат генератора коду можеприймати різні види: абсолютний машинний код, переміщуваний машинний код, абоасемблерна мова. Перевагою генерації абсолютної програми в машинному коді є те,що такий код поміщається у фіксоване місце пам’яті і негайно виконується.Невеликі програми при цьому швидко компілюються і виконуються.
Генерація переміщуваної програми умашинному коді (об’єктного модуля) забезпечує можливість роздільної компіляціїпідпрограм. Багато переміщуваних модулів можуть бути після цього зв’язані водне ціле і завантажені на виконання спеціальною програмою – завантажувачем.Додаткові затрати на зв’язування і завантаження компенсуються можливістюроздільної компіляції підпрограм і викликом інших, раніше скомпільованихпідпрограм із об’єктних модулів. Якщо цільова машина не обробляє переміщенняавтоматично, компілятор має надати завантажувачеві явну інформацю пропереміщення для зв’язування сегментів роздільно скомпільованиз підпрограм.
Отримання на виході генератора кодупрограми на мові асемблера трохи полегшує процес генерації коду; в результатіми можемо створювати символьні інструкції і використовувати можливості макросівасемблера. Плата за цю простоту – додатковий крок в обробці мови програмування асемблерпісля генерації коду.
4 Відладка та тестуваннякомпілятора 4.6.1 Виявленнялексичних помилок
Повідомлення про лексичну помилкувиводиться, коли лексичний аналізатор знаходить лексему, що не відповідаєлексиці мови програмування та ні одному з імен описаних користувачем змінних.Для перевірки розробленого компілятора на виявлення лексичних помилок внесемо втекст програми помилку – лексему BegAn.
'Error 13: Невідомий символ: BegAn '
З повідомлення стає зрозуміло, що вході компіляції було виявлено невідомий символ ’ BegAn’ в 2-ому рядку.
Під час роботи сканера може виникнутипомилка вище наведеного типу (тобто виявлено невідому лексему), а такожнеправильне оголошення ім’я змінної (коли першою є цифра).
Результат тестування в додатку Б. 4.6.2 Виявленнясинтаксичних помилок
Повідомлення про синтаксичну помилкувиводиться, коли синтаксичний аналізатор знаходить ланцюжок лексем, що невідповідає граматиці заданої мови. Для перевірки компілятора на виявленнясинтаксичних помилок пропустимо в тексті програми роздільник «;».
В результаті на екрані отримуємонаступні повідомлення:
Error15: Пропущено; пiсля операцiiwriteln'
З повідомлення випливає, що в ходікомпіляції було виявлено синтаксичну помилку – пропущено роздільник ’;’. Післяцього компіляцію було перервано.
Результат тестування в додатку Б.4.6.3 Виявлення семантичних помилок
Повідомлення про семантичну помилкувиводиться семантичним аналізатором, коли у виразі не співпадають типивикористовуваних змінних. Для перевірки компілятора на виявлення семантичнихпомилок внесемо в текст програми вираз з використанням змінних різних типів.Результат тестування в додатку Б.
'Error 18: Пропущено змінну: b'
З повідомлення випливає, що в ходікомпіляції було виявлено семантичну помилку – було виявлено неоголошену зміннуb. Після чого компіляцію було перервано.
Можливі наступні типи семантичніпомилок, що реалізовані в компіляторі:
1. Багатократнеоголошення;
2. Змінна неоголошена;
3. Змінна неініціалізована;
4. Неспівпадіння типів змінних. 4.6.4 Загальнаперевірка коректності роботи транслятора
Загальна перевірка полягає уздатності розробленого компілятора виконувати свої функції. Компілятор повинентранслювати програму у проміжне представлення на мові асемблер та створюватиоб’єктний і виконуваний файли за допомогою файлів tasm.exe та tlink.exe.Результат тестування в додатку Б.
Висновок
Під час виконання курсової роботи:
1. Складеноформальний опис мови програмування М13 у формі розширеної нотації Бекуса-Наура,дано опис усіх символів та ключових слів.
2. Створенокомпілятор мови програмування М13, а саме:
2.1.1.Розробленолексичний аналізатор, здатний розпізнавати лексеми, що є описані в формальномуописі мови програмування, та додані під час безпосереднього використаннякомпілятора.
2.1.2.Розробленосинтаксичний аналізатор на основі автомата з магазинною пам’яттю. Складенотаблицю переходів для даного автомата згідно правил записаних в нотації у форміБекуса-Наура.
2.1.3.Розробленогенератор коду, який починає свою роботу після того, як лексичним, синтаксичнимта семантичним аналізатором не було виявлено помилок у програмі, написаніймовою М13. Проміжним кодом генератора є програма на мові Assembler(i8086).Вихідним кодом є машинний код, що міститься у виконуваному файлі
3. Проведенетестування компілятора за допомогою тестових програм за наступними пунктами:
3.1.1.Виявленнялексичних помилок.
3.1.2.Виявленнясинтаксичних помилок.
3.1.3.Загальнаперевірка роботи компілятора.
Тестування не виявило помилок вроботі компілятора, а всі помилки в тестових програмах мовою М13 були виявленіі дано попередження про їх наявність.
В результаті виконання даної курсовоїроботи було успішно засвоєно методи розробки та реалізації компонент системногопрограмного забезпечення.
Списоквикористаної літератури
1. Бек Л.С.Введение в системное програмирование. – М.: Мир, 1988.
2. КасаткинМ.В. Касаткина Т.Я. Професиональное програмирование на языке С.В 3т. – М. Мир,1989.//т.3. Системное програмирование.
3. КузьминА.Я. Лексический анализ. – М.: ВШ.,1985.
4. ФроловА.В. Проэктирование компиляторов. –М.: Мир,1989.
5. СтрауструпБ. Введение в язык C++, 2001.
6. Ахо А.,Сети Р., Ульман Дж.Компиляторы: принципы, технологии, инструменты. – М.:Издательский дом «Вильямс», 2003.
7. ДжордейнР. Справочник программиста ПК IBM PC, XT/AT. – М.: ФиС, 1992.
8. Абель П.Ассемблер для IBM PC, 1991.
9. Прата С.Язык программирования Си, 2003
Додатки
Додаток A№ A B C D 13 1 (byte) ^ NOT while-do
program TFirst;
usesApp,dialogs,drivers,menus,objects,stddlg,views,validate;
type TMyApp=object(TApplication)
procedure InitStatusLine; virtual;
procedure InitMenuBar; virtual;
procedure NewWindow; virtual;
procedure HandleEvent(varEvent:TEvent);virtual;
procedure NewDialog;virtual;
{procedure Init;virtual;}
end;
DialogData=record
{CheckBoxData:Word;
RadioButtonData:word;}
InputLineData:string[128];
end;
MyStruct=record
b:integer;
b1:integer;
c:string[32];
end;
PM13Window=^TM13Window;
TM13Window=object(TWindow)
constructor Init(Bounds:TRect;
WinTitle:string;WindowNo:Word);
procedure MakeInterior(Bounds:TRect);
end;
var M13DialogData:DialogData;
s1:string;
const
MaxLines=2000;
var LineCount:integer;
Lines:array [0..MaxLines-1]ofPString;
type
PInterior=^TInterior;
TInterior=object(Tscroller)
constructor Init(varBounds:TRect;AHScrollBar,
AVScrollBar:PScrollBar);
procedure Draw;virtual;
end;
constcmNewWin=199;cmFileOpen=200;WinCount:Integer=0;cmCompile=201;
procedure TMyApp.InitStatusLine;
var r:TRect;
begin
{GetExtent(r);}
r.a.y:=r.b.y+1;
StatusLine:=New(PStatusLine,Init(r,
NewStatusDef(0,$ffff,
NewStatusKey('~Alt-X~ Exit', kbAltX,cmQuit,
NewStatusKey('~Alt-F4~ New', kbF4,cmNewWin,
NewStatusKey('~Alt-F3~ Close',kbAltF3, cmClose,
NewStatusKey( '',kbF10, cmMenu,
nil)))),
nil)));
end;
procedure TMyApp.InitMenuBar;
var r:TRect;
begin
GetExtent(r);
r.b.y:=r.a.y+1;
MenuBar:=New(PMenuBar, Init(r,NewMenu(
NewSubMenu('~F~ile', hcNoContext,NewMenu(
NewItem('~O~pen','F3',kbF3,cmFileOpen, hcNoContext,
NewItem('~N~ew', 'F4',kbF4,cmNewWin,hcNoContext,
NewLine(
NewItem('E~x~it', 'Alt-x', kbAltX,cmQuit, hcNoContext,
nil))))),
NewSubMenu('~W~indow', hcNoContext,NewMenu(
NewItem('~N~ext', 'F6',kbF6,cmNext,hcNoContext,
NewItem('~Z~oom', 'F5',kbF5,cmZoom, hcNoContext,
nil))),
NewSubMenu('~C~ompile', hcNoContext,NewMenu(
NewItem('~C~ompile','Alt-F9',kbAltF9,cmCompile,hcNoContext,
nil)),
nil
))))));end;
procedure TMyApp.NewWindow;
var Window:PM13Window;
r:TRect;
i:integer;
begin
i:=0;
inc(WinCount);
r.assign(0,0,80,23-wincount+1);
r.move(0,i+wincount-1);
window:=new(pM13window, init(r,'Compile window', wincount));
desktop^.insert(window);
end;
procedure TMyApp.NewDialog;
var
Dialog:PDialog;
R:TRect;
control:Word;
B:PView;
Window:PM13Window;
i:integer;
f:text;
s:string;
begin
R.Assign(20,6,60,19);
Dialog:=New(PDialog,Init(R,'M13Dialog'));
with Dialog^ do
begin
R.Assign(15,10,25,12);
Insert(New(PButton,Init(R,'~O~K',cmOK,bfDefault)));
R.Assign(28,10,38,12);
Insert(New(PButton,Init(R,'Cancel',cmCancel,bfNormal)));
R.Assign(3,8,37,9);
B:=New(PInputLine,Init(r,128));
insert(b);
R.Assign(2,7,24,8);
insert(New(PLabel,Init(R,'Deliveryinstructions',B)));
end;
Dialog^.SetData(M13DialogData);
Control:=DeskTop^.ExecView(Dialog);
if ControlcmCancel thenDialog^.GetData(M13DialogData);
i:=0;
whileM13DialogData.InputLineData[i]'.' do
begin
s[i]:=M13DialogData.InputLineData[i];
i:=i+1;
end;
s[i]:=M13DialogData.InputLineData[i];
s[i+1]:=M13DialogData.InputLineData[i+1];
s[i+2]:=M13DialogData.InputLineData[i+2];
s[i+3]:=M13DialogData.InputLineData[i+3];
s1:=s;
LineCount:=0;
Assign(F,s);
reset(F);
while not Eof(F) and(LineCount
begin
readln(f,s);
lines[linecount]:=newstr(s);
inc(linecount);
{writeln(lines[linecount]^);}
end;
close(F);
i:=0;
inc(WinCount);
r.assign(0,0,80,23-wincount+1);
r.move(0,i+wincount-1);
window:=new(pM13window, init(r, s1,wincount));
desktop^.insert(window);
end;
function vuraz(var s:char):integer;
var
w:integer;
begin
if((integer(s)>=97)and(integer(s)=65)and(integer(s)
then vuraz:=30
else vuraz:=29;
end;
procedure Compiles;
label aa;
var a1:array [1..100] of MyStruct;
i,j,j1,k,kk,i1,i2,var_kil,begin_kil,end_kil,var_index,begin_index,end_index:integer;
q,q1:string;
ss:array [1..50] of string[50];
f1,f2,f3,f4:text;
ch,ch1:char;
mn:string;
m,nerivne,m1,pa:integer;
begin
assign(f1,s1);
reset(f1);
i:=0;j:=0;
while s1[i]'.' do
begin
q[j]:=s1[i];q1[i]:=s1[i];
i:=i+1;j:=j+1;
end;
q[j]:=s1[i];j:=j+1;q[j]:='t';j:=j+1;q[j]:='x';j:=j+1;q[j]:='t';
q1[i]:=s1[i];q1[i+1]:='a';q1[i+2]:='s';q1[i+3]:='m';
for i:=1 to 100 do
a1[i].b:=0;
i1:=1;
assign(f2,q);
Rewrite(f2);
assign(f3,q1);
rewrite(f3);
j1:=1;k:=0;i:=1;
j:=1;
while not EOF(f1) do
begin
readln(f1,ss[j]);
a1[i].c[1]:=ss[j][1];j1:=2;ch1:=ss[j][1];
i2:=2;
while ss[j][i2]#0 do
begin
if (ss[j][i2]='')or(ss[j][i2]=';')or(ss[j][i2]='+')or(ss[j][i2]='-')or(ss[j][i2]='*')
or(ss[j][i2]='.')or(ss[j][i2]='/')or(ss[j][i2]=')')or(ss[j][i2]='(')or(ss[j][i2]=',')or(ss[j][i2]=':')
or(ss[j][i2]='=')or(ss[j][i2]='>')or(ss[j][i2]=''))
or((ss[j][i2]='=')and(ss[j][i2]='='))or(ss[j][i2]='^')
then begin
if (ch1='')or(ch1=';')or(ch1='+')or(ch1='-')or(ch1='*')
or (ch1='.')or(ch1='/')or(ch1=')')or(ch1='(')or(ch1=',')or(ch1=':')or(ch1='=')
or(ch1='>')or(ch1=''))
or ((ch1='=')and(ch1='='))or(ch1='^')
then begin i:=i+1;end
else begina1[i].c[j1]:=#0;i:=i+1;end;
ch1:=ss[j][i2];
a1[i].c[1]:=ss[j][i2];a1[i].c[2]:=#0;i:=i+1;j1:=1;
end
else begina1[i].c[j1]:=ss[j][i2];j1:=j1+1;ch1:=ss[j][i2];end;
i2:=i2+1;
end;
j:=j+1;
end;
k:=i-1;
for i:=1 to k do
begin
if ((a1[i].c[1]='p')and(a1[i].c[2]='r')and( a1[i].c[3]='o')and( a1[i].c[4]='g')
and( a1[i].c[5]='r')and(a1[i].c[6]='a')and( a1[i].c[7]='m'))then a1[i].b:=1;
if (( a1[i].c[1]='v')and(a1[i].c[2]='a')and( a1[i].c[3]='r'))then a1[i].b:=2;
if (( a1[i].c[1]='b')and(a1[i].c[2]='y')and( a1[i].c[3]='t')and( a1[i].c[4]='e'))then a1[i].b:=3;
if (( a1[i].c[1]='c')and(a1[i].c[2]='h')and( a1[i].c[3]='a')and( a1[i].c[4]='r'))then a1[i].b:=4;
if (( a1[i].c[1]='b')and(a1[i].c[2]='o')and( a1[i].c[3]='o')and( a1[i].c[4]='l')
and( a1[i].c[5]='e')and(a1[i].c[6]='a')and( a1[i].c[7]='n'))then a1[i].b:=5;
if (( a1[i].c[1]='b')and(a1[i].c[2]='e')and( a1[i].c[3]='g')and( a1[i].c[4]='i')
and( a1[i].c[5]='n'))then a1[i].b:=7;
if (( a1[i].c[1]='w')and(a1[i].c[2]='r')and( a1[i].c[3]='i')and( a1[i].c[4]='t')
and( a1[i].c[5]='e')and(a1[i].c[6]='l')and( a1[i].c[7]='n'))then a1[i].b:=8;
if (( a1[i].c[1]='r')and(a1[i].c[2]='e')and( a1[i].c[3]='a')and( a1[i].c[4]='d')
and( a1[i].c[5]='l')and(a1[i].c[6]='n'))then a1[i].b:=9;
if (( a1[i].c[1]='e')and(a1[i].c[2]='n')and( a1[i].c[3]='d'))then a1[i].b:=15;
if (a1[i].c[1]=';')then a1[i].b:=16;
if (a1[i].c[1]=',')then a1[i].b:=17;
if (a1[i].c[1]='(')then a1[i].b:=18;
if (a1[i].c[1]=')')then a1[i].b:=19;
if (a1[i].c[1]='*')then a1[i].b:=20;
if (a1[i].c[1]='+')then a1[i].b:=21;
if (a1[i].c[1]='-')then a1[i].b:=22;
if (a1[i].c[1]='/')then a1[i].b:=23;
if (a1[i].c[1]=' ')then a1[i].b:=24;
if (a1[i].c[1]='\n')then a1[i].b:=25;
if (a1[i].c[1]=':')then a1[i].b:=26;
if (a1[i].c[1]='=')then a1[i].b:=27;
if (a1[i].c[1]='.')then a1[i].b:=28;
if (a1[i].c[1]='>')then a1[i].b:=29;
if (a1[i].c[1]='
if((a1[i].c[1]='!')and(a1[i].c[1]='=')) then a1[i].b:=31;
if((a1[i].c[1]='=')and(a1[i].c[1]='=')) then a1[i].b:=32;
if (a1[i].c[1]='^')then a1[i].b:=33;
if (( a1[i].c[1]='N')and(a1[i].c[2]='O')
and( a1[i].c[3]='T'))thena1[i].b:=34;
end;
for i:=0 to k do
if a1[i].b=0 thena1[i].b:=vuraz(a1[i].c[1]);
var_kil:=0;begin_kil:=0;end_kil:=0;
for i:=1 to k do
begin
if a1[i].b=2 then var_kil:=var_kil+1;
if a1[i].b=7 thenbegin_kil:=begin_kil+1;
if a1[i].b=15 thenend_kil:=end_kil+1;
end;
for i:=1 to k do
begin
writeln(f2,a1[i].b);
end;
if var_kil>1 thenwriteln(f2,'Error 1: Декiлька разiв вiдбуваеться опис даних');
if begin_kil>1 thenwriteln(f2,'Error 2: Декiлька разiв введено слово begin');
if end_kil>1 thenwriteln(f2,'Error 3: Декiлька разiв введено слово end');
if var_kil=0 then writeln(f2,'Error4: Немає опису даних');
if begin_kil=0 then writeln(f2,'Error5: Немає початку тiла програми');
if end_kil=0 then writeln(f2,'Error6: Немає кiнця тiла програми');
if nevid=0 then writeln(f2,'Error 18:Невідома змінна:',k);
var_index:=0;begin_index:=0;end_index:=0;
for i:=1 to k do
begin
if a1[i].b=2 then var_index:=i;
if a1[i].b=7 then begin_index:=i;
if a1[i].b=15 then end_index:=i;
end;
for i:=var_index to begin_index do
begin
if a1[i].b=3 then begin ifa1[i-1].b26 then
writeln(f2,'Error 7: Пропущено-: приописi змiнних типу Byte');
j:=i-2;
while a1[j].b16 do
begin
if a1[j].b=30 then a1[j].b:=35;
j:=j-1;
end;
end;
if a1[i].b=4 then begin ifa1[i-1].b26then
writeln(f2,'Error 7: Пропущено-: приописi змiнних типу Char');
j:=i-2;
while a1[j].b26 do
begin
if a1[j].b=30 then a1[j].b:=36;
j:=j-1;
end;
end;
if a1[i].b=5 then begin ifa1[i-1].b16then
writeln(f2,'Error 7: Пропущено-: приописi змiнних типу Boolean');
j:=i-2;
while a1[j].b16 do
begin
if a1[j].b=30 then a1[j].b:=37;
j:=j-1;
end;
end;
end;
assign(f4,'1.M13');
reset(f4);
readln(f4,mn);
writeln(f3,'ideal');
writeln(f3,'model small');
writeln(f3,'stack 256');
writeln(f3,'dataseg');
writeln(f3,'perkur db ',mn,'$',mn);
writeln(f3,'TrueStr db',mn,'TRUE$',mn);
writeln(f3,'FalseStr db',mn,'False$',mn);
writeln(f3,'InString db 255 DUP(?)');
writeln(f3,'OutString db 255DUP(?)');
writeln(f3,'CharStr db 2,5 DUP(?)');
writeln(f3,'len dw ?');
for i:=var_index+1 to begin_index do
begin
if a1[i].b=31 then begin j:=1;
while a1[i].c[j]#0 do
begin
write(f3,a1[i].c[j]);
j:=j+1;
end;
writeln(f3,' dw ?');
end;
if a1[i].b=32 then begin j:=1;
while a1[i].c[j]#0 do
begin
write(f3,a1[i].c[j]);
j:=j+1;
end;
writeln(f3,' db ?');
end;
if a1[i].b=33 then begin j:=1;
while a1[i].c[j]#0 do
begin
write(f3,a1[i].c[j]);
j:=j+1;
end;
writeln(f3,' db ?');
end;
end;
writeln(f3,'codeseg');
writeln(f3,'start:');
writeln(f3,'mov ax,@data');
writeln(f3,'mov ds,ax');
i:=begin_index+1;
while iend_index do
begin
kk:=i;
if a1[i].b=8
then
begin
if (a1[i+1].b18)thenwriteln(f2,'Error: Пропущено ( при написаннi функцii writeln');
kk:=i+2;
while a1[kk].b19 do
begin
if (a1[kk].b17) or(a1[kk].b19) then
begin
for j:=var_index+1 to begin_index do
begin
if(copy(a1[kk].c,1,a1[kk].b1))=(copy(a1[j].c,1,a1[j].b1))
then a1[kk].b:=a1[j].b;
end;
writeln(a1[kk].b);
if a1[kk].b=31 then
begin
writeln(f3,'mov ax,[',copy(a1[kk].c,1,a1[kk].b1),']');
writeln(f3,'call WriteByte');
end;
if a1[kk].b=32 then
begin
writeln(f3,'moval,[',copy(a1[kk].c,1,a1[kk].b1),']');
writeln(f3,'call WriteChar');
end;
if a1[kk].b=33 then
begin
writeln(f3,'moval,[',copy(a1[kk].c,1,a1[kk].b1),']');
writeln(f3,'call WriteBool');
end;
end;
kk:=kk+1;
end;
if a1[kk+1].b16 then ifa1[kk+2].b16 then
writeln(f2,'Error: Пропущено; пiсляоперацii writeln');
end;
if a1[i].b=9
then
begin
if (a1[i+1].b18)thenwriteln(f2,'Error: Пропущено ( при написаннi функцii writeln');
kk:=i+2;while a1[kk].b19 do
begin
if a1[kk].b17 then
begin
m:=0;while a1[kk].c[m]#0 do
m:=m+1;
m:=m+1;
for j:=var_index+1 to begin_index do
begin
nerivne:=0;m1:=0;
while m1m do
begin
if a1[kk].c[m1]=a1[j].c[m1] thennerivne:=nerivne+1;
m1:=m1+1
end;
if nerivne=m then a1[kk].b:=a1[j].b;
end;
if a1[kk].b=31 then
begin
writeln(f3,'call ReadInt');
writeln(f3,'mov [',a1[kk].c,'],ax');
end;
if a1[kk].b=32 then
begin
writeln(f3,'call ReadChar');
writeln(f3,'mov [',a1[kk].c,'],al');
end;
if a1[kk].b=33 then
begin
writeln(f3,'call ReadBool');
writeln(f3,'mov [',a1[kk].c,'],al');
end;
end;
kk:=kk+1;
end;
if a1[kk+1].b16 then ifa1[kk+2].b16 then
writeln(f2,'Error 15: Пропущено;пiсля операцii writeln');
end;
{if a1[i].b=30 then pa:=assign(a1,i);}
i:=kk+1;
end;
close(f3);
close(f2);
close(f1);
writeln('Кiнець');
end;
procedure TMyApp.HandleEvent(varEvent:TEvent);
begin
TApplication.HandleEvent(Event);
if Event.What=evCommand then
begin
case Event.Command of
cmNewWin:NewWindow;
cmFileOpen:NewDialog;
cmCompile:Compiles;
else
Exit;
end;
ClearEvent(Event);
end;
end;
constructor TInterior.Init(varBounds:TRect;AHScrollBar,
AVScrollBar:PScrollBar);
begin
TScroller.Init(Bounds,AHScrollBar,AVScrollBar);
GrowMode:=gfGrowHiX+gfGrowHiY;
SetLimit(128,LineCount);
end;
procedure TInterior.Draw;
var i,y:integer;
Color:Byte;
B:TDrawBuffer;
begin
Color:=GetColor(1);
for y:=0 to size.y-1 do
begin
MoveChar(B,' ',Color,Size.x);
i:=Delta.Y+y;
if (inil) then
MoveStr(B,Copy(Lines[i]^,Delta.x+1,Size.x),Color);
writeLine(0,y,Size.x,1,b);
end;
end;
procedure ReadFile;
var
F:Text;
S:String;
begin
LineCount:=0;
Assign(F,'Test.M13');
reset(F);
while not Eof(F) and(LineCount
begin
readln(f,s);
lines[linecount]:=newstr(s);
inc(linecount);
{writeln(lines[linecount]^);}
end;
close(F);
end;
procedureTM13Window.MakeInterior(Bounds:Trect);
var
HScrollBar,VScrollBar:PScrollBar;
interior:PInterior;
R:TRect;
begin
VScrollBar:=StandardScrollBar(sbVertical+sbHandleKeyboard);
HScrollBar:=StandardScrollBar(sbHorizontal+sbHandleKeyboard);
Interior:=New(PInterior,Init(Bounds,HScrollBar,VScrollBar));
Insert(Interior);
end;
constructorTM13Window.Init(Bounds:TRect;
WinTitle:string;WindowNo:Word);
var s:string[3];
begin
str(WindowNo, s);
TWindow.Init(Bounds, WinTitle+' '+s,wnNoNumber);
GetExtent(Bounds);
Bounds.Grow(-1,-1);
MakeInterior(Bounds);
end;
procedure DoneFile;
var
i:integer;
begin
for i:=0 to linecount-1 do
if lines[i]nil thendisposestr(lines[i]);
end;
var MyApp:TMyApp;
begin
MyApp.Init;
MyApp.Run;
MyApp.Done;
end.
Додаток Б
Тестова програма на мові M13з лексичною помилкою:
'Error 13: Невідомий символ: BegAn '
program ja;
var a:byte;
begАn
a:=13;
writeln ('Varian :', a);
end.
Тестова програма на мові M13з синтаксичною помилкою:
Error15: Пропущено; пiсля операцiiwriteln'
program ja;
var a:byte;
begin
a:=13;
writeln ('Varian :', a)
end.
Тестова програма на мові M13з семантичною помилкою:
'Error 18: Пропущенозмінну: b'
program ja;
var a,c:byte;
begАn
a:=53;
b:=13;
c:=a+b;
writeln ('Varian :', b)
end.
Тестова програма на мові M13без помилок:
program ja;
var a:byte;
begin
b:=13;
writeln ('Varian :', b);
end.
/>
Рисунок 6 Вигляд працездатногокомпілятора.