Національний університет “Львівська політехніка”
Інститут комп’ютерної техніки, автоматики та метрології
Кафедра “Електронні обчислювальні машини”
Курсова робота
з предмету “Системне програмування”
на тему: Розробка транслятора з вхідної мови програмування.
Варіант № 45
Анотація
В даній курсовій роботі розроблено транслятор з вхідної мови програмування z45. Дана робота носить навчальний характер, але є важливою, оскільки розробка програмних модулів і компонент системного програмування є актуальним завданням на сьогоднішній час. Транслятор розроблений в середовищі C++ Builder 2010 та поданий у пояснювальній записці, а також в електронному варіанті. Для побудови транслятора використано низхідний метод граматичного розбору. В пояснювальній записці подано огляд існуючих методів розробки трансляторів, детальний опис мови, а також описано процес розробки програми транслятора на рівні тексту програми. До проекту додано результати тестування програми та вихідний текст програми транслятора.
Зміст
Завдання на кусрову роботу 4
Вступ 6
1. Огляд методів та способів проектування трансляторів 7
2. Формальний опис вхідної мови програмування 9
2.1 Деталізований опис вхідної мови в термінах EBNF 9
2.2 Опис термінальних символів та ключових слів 11
3. Розробка транслятора вхідної мови програмування 12
3.1 Проектування таблиць транслятора 13
3.2 Розробка лексичного аналізатора 14
3.2.1 Розробка граф-схеми алгоритму 16
3.2.2 Опис програми реалізації лексичного аналізатора 18
3.3 Опис синтаксичного та семантичного аналізатора 20
3.3.1 Дерево граматичного розбору 21
3.3.2 Розробка граф-схеми алгоритму 22
3.3.3 Опис програми реалізації синтаксичного та семантичного аналізатора 23
3.4 Розробка генератора коду 24
3.4.1 Розробка граф-схеми алгоритму 25
3.4.2 Опис програми реалізації генератора коду 26
4. Опис інтерфейсу та інструкції користувача 27
5. Відлагодження та тестування програми 28
5.1 Виявлення лексичних помилок 28
5.2 Виявлення синтаксичних помилок 29
5.3 Виявлення семантичних помилок 30
5.4 Загальна перевірка коректності роботи транслятора 31
Висновки 32
Список літератури 33
Додаток А лістинг програми транслятора 34
Додаток Б лістинг програми заготовки початкового коду 62
Завдання на курсову роботу
Розробити транслятор заданої вхідної мови програмування, до якої висуваються наступні вимоги:
Кожна програма починається зі слова program і закінчується словом end. Після program йде розділ оголошення змінних var після розділу оголошення змінних починається блок коду словом begin.. Програма має надавати можливість працювати зі змінними таких типів: integer_2 – цілі числа. Регістр ключових слів нижній. Регістр ідентифікаторів нижній та верхній при цьому перший символ повинен бути ‘_’ , а довжина ідентифікаторів не повинна бути більшою за вісім символів.
Присвоєння змінних виконується оператором присвоєння >> . Наприклад _X >> _Y + 5;
Над змінними виконуються наступні операції.
Арифметичні: +, -, *, /, % додавання, віднімання, ділення, множення, залишок від ділення;
Логічні: not, and, or.
Порівняння: =, <>, >=, <= рівність, нерівність, більше або рівно , менше або рівно;
Ввід даних зі стандартного вводу відбувається оператором scandata, а вивід оператором printdata. Наприклад scandata(_a); printdata(“A=”); printdata(_a);.
Для реалізації циклів виконується оператор for
Наприклад for( _x>>0 ; _x<=4; _x>>_x+1;){ блок коду }
Коментування певних частин коду здійснюється оператором коментаря // КОМЕНТАР.
Потрібно також розробити середовище, яке б включало текстовий редактор з можливістю набору, редагування програми; зчитування та запису її на диск, запуску трансляції.
На вхід розробленого компілятора має подаватися текстовий файл, написаний на заданій мові програмування. На виході розробленого компілятора мають з’являтися три файли:
файл з результатом лексичного аналізу,
файл з результатом синтаксичного аналізу,
файл з результуючим програмним кодом на мові асемблер,
файл з інформацією про помилки, якщо вони є,
об’єктний файл,
виконавчий файл
Вступ
Створення трансляторів є одною з невід’ємних частин системного програмного забезпечення. Одним із завдань транслятора є переведення написаного тексту програми у машинний код, який повинен відповідати комп’ютерній системі. Оскільки комп’ютерна галузь на сьогоднішній день розвивається дуже швидко, то створений машинний код з часом стає застарілим, тобто не відповідає принципу оптимального використання комп’ютерних ресурсів. Тому для запобігання цього явища необхідно створювати нові компілятори, які б відповідали потребам ринку.
Проблема трансляції полягає в пошуку відповідності тексту вхідної програми конструкціям, що визначені граматикою. Граматика визначає форму або синтаксис допустимих виразів мови. Тому текст вхідної мови зручно подавати у вигляді послідовності лексем, що є неподільними одиницями мови. За допомогою транслятора програміст повинен мати можливість редагувати текст вхідної мови. Для цього транслятор має виявляти всі невідповідності тексту програми конструкціям мови і у випадку відсутності помилок генерувати об'єктний код або виконавчий модуль.
В даній курсовій роботі розроблятиметься транслятор з вхідної мови програмування . Програма-транслятор повинна буде з заданого на вході коду програми на вхідній мові сформувати код на мові асемблера. Компіляція асемблерного коду здіснуватиметься компілятором MASM 32. Також буде створено середовище розробки, що дозволяє створювати та підлагоджувати програму. Середовище розробки представлятиме з себе текстовий редактор з інтерфейсом SDI, де крім стандартних командних меню будуть командні меню для виконання всіх фаз трансляції. Також з метою поліпшення від лагодження буде додана можливість підсвічення і виділення помилок. Всі проміжні результати розробки програми будуть зберігатися в відповідний файлах в каталозі з файлом з вхідною програмою.
Огляд методів та способів проектування трансляторів
На перший погляд, різноманітність компіляторів вражає. Використовуються тисячі вихідних мов, від традиційних, таких як Fortran і Pascal, до спеціалізованих, які виникають у всіх областях застосування комп’ютера. Цільові мови не менш різноманітні – це можуть бути інші мови програмування, різні машинні мови – від мов мікропроцесорів до суперкомп’ютерів. Компілятори класифікують як однопрохідні, багато прохідні, виконуючі (load-and-go), відлагоджуючі, оптимізуючи – в залежності від призначення і принципів і технологій їх створення.
Не дивлячись на те, що основні задачі, що виконуються компіляторами видаються складними і різноманітними, по суті вони одні і ті ж. Розуміючи ці задачі, ми можемо створювати компілятори для різних вихідних мов і цільових машин з використанням одних і тих же базових технологій.
В 50х роках про компілятори ходила слава, що це програми, дуже складні в написанні (наприклад, перший компілятор Fortran потребував 18 людино-років роботи). З того часу розроблені різноманітні систематичні технології вирішення багатьох задач, виникаючих при компіляції. Крім цього, розроблені хороші мови реалізації, програмні середовища та програмні інструменти. Завдяки цьому «солідний» компілятор може бути реалізований в якості курсової роботи з проектування компіляторів.
Компіляція складається з двох частин: аналізу і синтезу. Аналіз – це розбиття початкової програми на складові частини і створення її проміжного представлення. Синтез – конструювання необхідної цільової програми з проміжного представлення.
В процесі аналізу визначаються і записуються в ієрархічну деревовидну структуру операції, задані початковою програмою. Часто використовується спеціальний вид дерева, що називається синтаксичним (або деревом синтаксичного розбору), в якому кожен вузол представляє операцію, а його дочірні вузли – аргументи операції.
Багато програмних інструментів, працюючи з початковими програмами, спочатку виконують певний вид аналізу. Розглянемо приклади таких інструментів.
Структурні редактори. Ці програми одержують як вхід послідовність команд для побудови початкової програми. Такий редактор не тільки виконує звичні для текстового редактора функції зі створення і модифікації тексту, але і аналізує текст програми, поміщаючи в початкову програму відповідну ієрархічну структуру. Тим самим він виконує додаткові задачі, що полегшують підготовку програми. Наприклад, редактор може перевіряти коректність введеного тексту, автоматично додавати структурні елементи (так, якщо користувач введе while, редактор додасть відповідне йому ключове слово do і запропонує ввести умовний вираз між ними) або переходить від ключового слова begin або лівої дужки до відповідного end або правої дужки. Більше того, результат на виході такого редактора часто подібний результату після фази аналізу компіляції.
Програми форматованого виводу для друку. За допомогою цих інструментів програма аналізується і роздруковується так, щоб її структура була максимально ясною. Наприклад, коментарі можуть бути виділені спеціальним шрифтом, а оператори – виведені з відступами, що вказують рівень вкладеності в ієрархічній структурі операторів.
Статичні перевіряючі програми. Дані інструменти зчитують програми, аналізують їх і намагаються знайти потенційні помилки без запуску програми. Такий аналіз часто дуже схожий на аналіз в оптимізуючих компіляторах. Наприклад, статична перевіряюча програма може визначити невиконання якоїсь частини початкової програми або використання деякої змінної до її оголошення. Так само можуть бути знайдені логічні помилки, наприклад спроби використання дійсної змінної як покажчик (із застосуванням технології перевірки типів).
При створенні цільової програми, окрім компілятора, може бути потрібним і ряд інших програм. Початкова програма може бути розділена на модулі, що зберігаються в окремих файлах. Задача збору початкової програми іноді доручається окремій програмі – препроцесору, який може також розкривати в тексті початкової програми скорочення, так звані макроси.
Цільова програма, створювана компілятором, може зажадати додаткову обробку перед запуском. Компілятор, створює асемблерний код, який переводиться асемблером в машинний код, а потім зв'язується («лінкуєтся») спільно з деякими бібліотечними програмами в код, що реально запускається на машині.
Формальний опис вхідної мови програмування
Форма Бе́куса-Нау́ра (англ. Backus-Naur form, BNF) - це спосіб запису правил контекстно-вільної граматики, тобто це форма опису формальної мови.
Одною з перших задач, що виникають при побудові транслятора, є визначення вхідної мови програмування. Для цього використовують різні способи формального опису. В курсовій роботі використано розширену нотацію Бекуса-Наура (Backus/Naur Form - BNF).
2.1 Деталізований опис вхідної мови в термінах EBNF
Деталізований опис вхідної мови потрібен для того , щоб користувач знав, яким чином працює програма , і які компоненти можна в ній використовувати.
Опис здійснюється за допомогою правил.Дані правила потрібні для того, щоб ми могли чітко описати правила написання вхідної мови програмування. Для опису заданої вхідної мови використаємо розширену нотацію Бекуса-Наура, з наступними правилами написання правил:
Нетермінальні вирази записуються у кутових дужках: “<”, “>” ;
Термінальні вирази записуються жирним шрифтом або у подвійних лапках;
Усі нетермінальні вирази мають бути “розкриті” за допомогою термінальних;
Символ “::=” відділяє праву частину правила від лівої;
символ “|” розділяє альтернативи;
символи “[”, “]” означають необов’язковість (вираз в дужках може бути відсутнім);
символи “{”, “}” означають повторення.
Деталізований опис вхідної мови описаний у таблиці 2.1.
Таблиця 2.1 Деталізований опис вхідної мови
<program>
::= program < <nameprog> > <block>
<nameprog>
::= <name>{<name>} ;
<block>
::= var <data_block> begin <code_block> end
<name>
::= <l_letter>|<h_letter>|<number>
<data_block>
::= <ident> { , <ident>} integer_2 ;
<ident>
::= _<letter>
<letter>
::= <l_letter>|<h_letter>
<l_letter>
::= a|b|c|d|e|f|g|h|i|j|k|l|n|m|o|p|q|r|s|t|u|v|w|x|y|z
<h_letter>
::= A|B|C|D|E|F|G|H|I|J|K|L|N|M|O|P|Q|R|S|T|U|V|W|X|Y|Z
<number>
::= 0|1|2|3|4|5|6|7|8|9
<string>
::= “ {<l_letter> | <h_letter> | <number> } “
<code_block>
::= {<statement>}
<statement>
::= <assign> | <cycle> | <scan> | <print>
<assign>
::= <ident> >> <expression>;
<equality>
::= = | <>
<comparison>
::= <= | >=
<additional>
::= + | -
<multiplication>
::= * | / | %
<expression>
::= <atom> { or <atom>}
<atom>
::= <eq> { and <eq>}
<eq>
::= <comp> {<equality><comp>}
<comp>
::= <term> {<comparison><term>}
<term>
::= <mult> {<additional> <mult>}
<mult>
::= <operand> {<multiplication> <operand>}
<operand>
::= <ident> | <const> | (<expression>)| not <ident> | not<const> |not (<expression>)
<const>
::= [-] <number>{<number>}
<cycle>
:: <for> ‘{‘ <code_block> ‘}’
<for>
::= for <cycl_expression>
<cycl_expression>
::= ( <assign> ; <expression> ; <assign> ;)
<scan>
::= scandata ( <ident> );
<print>
::= printdata ( <expression> ) | printdata ( <string> );
2.2 Опис термінальних символів та ключових слів
Опис термінальних сиволів та ключових слів використовується для того, щоб користувач бачив , які символи та ключові слова можна використовувати у програмі.
Згідно варіанту, ми визначаємо окремі термінальні символи та нерозривні набори термінальних символів (ключові слова):
program var begin end ; + - * / % >> = <> >= <= not and or printdata scandata for { } ( )
До термінальних символів треба віднести також усі цифри (0..9), символ пробілу і табуляції. Це “цеглинки”, з яких має будуватися текст будь-якої вхідної програми.
Типи даних integer_2;
Арифметичні операції над числами +, -, /, *, %;
Логічні операції =, <>, >=, <=, not, and, or;
Оператор присвоєння значень змінним >> ;
Круглі дужки ( );
Фігурні дужки { };
Оператори вводу-виводу printdata, scandata;
Оператор оголошення змінних var ;
Початок програми program ;
Позначення початку і кінця блоку програми begin та end;
Оператор циклу for ( ) { }
3. Розробка транслятора вхідної мови
Для розробки даної програми (транслятора) потрібно спочатку скласти алгоритм роботи програми і її написання. Вибрати технологію програмування тобто: типи даних, структури, алгоритми які використовуючи в написанні прграми забезпечили б оптимальну кількість коду, його ефективність і зрозумілість.
Тому робимо аналіз нашої програми. Для роботи з балансом дужок чи операторів, таких операторів як for(){}; нам знадобиться структура стек. Тому при розробці буде використано структуру даних stack з стандартних бібліотек.
Для полегшення роботи з типами використовуємо тип enum для перерахування всіх можливих типів і полегшення наступного опрацювання лексем.
Для полегшення обробки лексем також використовуємо структуру Lex для зберігання імені лексеми, значення (якщо це число), її типу та рядка вхідного файлу (для полегшення обробки помилок).
Також використовуємо структуру даних список list для зберігання робочих даних програми – таблиці лексем, таблиці ідентифікаторів, деяких допоміжних змінних, необхідних для використання у різних функціях програми, і буфер для виразу в постфіксній формі. ( для кожної з таблиць окремий список).
Оскільки великі частини коду часто повторюються, то потрібно розбити його на підпрограми, що зобезпечить нам скороченя тексту програми і його розуміння. Наприклад GetLexema(), IsExpression(), Codegen() тощо..
Використання даних структур повинно спростити написання коду програми і полегшити її розуміння і читабельність, тому, технологія програмування вибрана вірно.
3.1. Проектування таблиць транслятора.
Використання таблиць значно полегшує створення трансляторів, тому у даному випадку використовуються кілька таблиць:
Таблиця лексем з елементами, які мають таку структуру:
enum LexemType
{
nameprogram_, program_, variable_, int_, begin_, end_, scandata_,
printdata_, for_, openBracket_, closeBracket_, newval_, add_, sub_, mul_, div_,
mod_, equ_, notequ_, le_, ge_, not_, and_, or_, eof_, EndGroup_, komma_,
ident_, number_, LeftBraket_, RightBraket_, string_, unknown_
};
struct Lex {
char name[50];
int value;
LexemType type;
int line;
};
std::list<Lex>lexem;
2) Таблиця ідентифікаторів . Структура елементів така:
struct Ident {
char name[50];
int value;
};
std::list<Ident>ident;
3) Таблиця константних буквенних рядків (наприклад для команди printdata (“Hello world”):
struct STR
{
char str[256];
int nc;
};
std::list<STR>tstr;
3.2. Розробка лексичного аналізатора.
Лексичний аналізатор є першою фазою компілятора. Основна задача лексичного аналізу – розбити вихідний текст, що складається з послідовності одиночних символів, на послідовність слів, або лексем, тобто виділити ці слова з безперервної послідовності символів для передачі в синтаксичний аналізатор. Всі символи вхідної послідовності розділяються на символи, що належать яким-небудь лексемам, і символи, що розділяють лексеми.
Основна задача лексичного аналізу – розбити вихідний текст, що складається з послідовності одиночних символів, на послідовність слів, або лексем, тобто виділити ці слова з безперервної послідовності символів. Всі символи вхідної послідовності з цієї точки зору розділяються на символи, що належать яким-небудь лексемам, і символи, що розділяють лексеми. В цьому випадку використовуються звичайні засоби обробки рядків. Вхiдна програма проглядається послідовно з початку до кінця. Базовi елементи, або лексичнi одиницi, роздiляються пробілами, знаками операцiй i спецiальними символами (новий рядок, знак табуляції), i таким чином видiляються та розпізнаються iдентифiкатори, лiтерали i термiнальнi символи (операцiї, ключові слова).
При виділенні лексеми вона розпізнається та записується у таблицю лексем за допомогою відповідного номера лексеми, що є унікальним для кожної лексеми із усього можливого їх набору. Це дає можливість наступним фазам компiляції звертатись лексеми не як до послідовності символів, а як до унікального номера лексеми, що значно спрощує роботу синтаксичного аналізатора: легко перевіряти належність лексеми до відповідної синтаксичної конструкції та є можливість легкого перегляду програми, як вгору, так і вниз, від текучої позиції аналізу. Також в таблиці лексем ведуться записи, щодо рядка відповідної лексеми – для місця помилки – та додаткова інформація.
При лексичному аналiзі виявляються i вiдзначаються лексичнi помилки (наприклад, недопустимi символи i неправильнi iдентифiкатори). Лексична фаза вiдкидає також i коментарi, оскiльки вони не мають нiякого впливу на виконання програми, отже ж й на синтаксичний розбір та генерацію коду.
Лексичний аналізатор (сканер) не обов’язково обробляє всю програму до початку всіх інших фаз. Якщо лексичний аналіз не виділяється як окрема фаза трансляції, а є частиною синтаксичного аналізу, то лексична обробка тексту програми виконується по мірі необхідності по запиту синтаксичного аналізатора.
В даному курсовому проекті реалізовано прямий лексичний аналізатор, який виділяє з вхідного тексту програми окремі лексеми і на основі цього формує таблицю. На рисунках 1 і 2-4 показано блок-схеми лексичного аналізу
enum LexemType
{
nameprogram_, program_, variable_, int_, begin_, end_, scandata_,
printdata_, for_, openBracket_, closeBracket_, newval_, add_, sub_, mul_, div_,
mod_, equ_, notequ_, le_, ge_, not_, and_, or_, eof_, EndGroup_, komma_,
ident_, number_, LeftBraket_, RightBraket_, string_, unknown_
};
struct Lex {
char name[50];
int value;
LexemType type;
int line;
};
struct Ident {
char name[50];
int value;
};
struct STR
{
char str[256];
int nc;
};
std::list<Lex>lexem;
std::list<Ident>ident;
std::list<STR>tstr;
3.2.1 Розробка граф-схеми алгоритму
Рис.1 Блок схема лексичного аналізатора.
Далі наведена блок-схема функції яка визначає та повертає лексему.
Опис програми реалізації лексичного аналізатора
Алгоритм роботи лексичного аналізатора складається з 4 блоків. В першому блоці відбувається входження в підпрограму. Далі, в другому блоці, викликається функція яка повертає тип наступної лексеми , ця лексема заноситься в таблицю лексем, якшо повернута лексема відповідає кінцю рядка вийти з підпрограми.
Функція яка повертає тип лексеми на вході отримує наступний символ, далі цей символ перевіряється з усіма визначеними лексемами якшо є співпадіння повертається повертається відповідний тип лексеми , якщо тип не визначений повертається unknow_.
3.3.Розробка синтаксичного аналізатора
Синтаксичний аналіз – це процес, в якому досліджується послідовність лексем, яку повернув лексичний аналізатор, і визначається, чи відповідає вона структурним умовам, що сформульовані у визначені синтаксису мови.
Синтаксичний аналізатор – це частина транслятора, яка відповідає за виявлення основних синтаксичних конструкцій вхідної мови. В задачу синтаксичного аналізатора входить: знайти та виділити основні синтаксичні конструкції мови в послідовності лексем програми, встановити тип та правильність побудови кожної синтаксичної конструкції і представити синтаксичні конструкції у вигляді, зручному для подальшої генерації тексту результуючої програми. Як правило, синтаксичні конструкції мови програмування можуть бути описані за допомогою контекстно-вільних граматик. Розпізнавач дає відповідь на питання, належить чи ні, ланцюжок вхідних лексем заданій мові.
В даному трансляторі синтаксичний аналізатор працює паралельно з семантичним, частиною якого він і є. Принцип роботи такий: на основі нижче наведеного дерева граматичного розбору(рис.5) аналізується черговий набір послідовних лексем. Потім знову вище описані дії повторюються поки не обробляться всі лексеми.
На рисунку 6 наведено блок-схему роботи синтаксичного аналізатора.
3.3.1 Розробка дерева граматичного розбору
Рис.5 Дерево граматичного розбору
3.3.2 Розробка граф-схеми алгоритму
Рис.6 Блок-схема синтаксичного аналізатора
3.3.3 Опис програми реалізації синтаксичного та семантичного аналізатора
Для аналізу синтаксичний аналізатор отримує на вхід послідовність лексем сформовану лексичним аналізатором. Спочатку перевіряється заголовок програми: початок головного блоку, розділ оголошення змінних, після цього відбувається перевірка коректності команд розміщених в головному блоці і вкладених блоках. Команди розпізнаються по черговій лексемі, відбувається зчитування ланцюжка лексем до крапки з комою. Прочитаний ланцюжок передається на перевірку певній процедурі аналізу , яка проводить аналіз і при виявленні помилки формує відповідний результат.
Проміжним представленням програми є списки лексем які представляють конкретні вирази, оператори, мітки. Вирази представлені зворотнім польським записом.
При знаходженні помилки в список помилок додається відповідний запис з інформацією про тип помилки і місце її локалізації в коді програми . Список помилок є глобальним і доступним в будь-якому місці програми.
3.4.Розробка генератора коду
Генерація коду – це машинно-залежний етап трансляції, під час якого відбувається побудова машинного еквівалента вхідної програми. Зазвичай входом для генератора коду служить проміжна форма представлення програми, а на виході може з’являтися об’єктний код або модуль завантаження.
У транслятора, реалізованого в даній курсовій роботі, вихідна мова - програма на мові Assembler. Ця програма записується у файл, що має таку ж саму назву, як і файл з вхідним текстом, але розширення “asm”. Генерація коду відбувається одразу ж після синтаксичного аналізу.
3.4.1 Розробка граф-схеми алгоритму
3.4.2 Опис програми реалізації генератора коду
Генерація коду відбувається поетапно. Генератор коду аналізує проміжне представлення (списки лексем) сформовані синтаксичним аналізом і генерує для них відповідний асемблерний код.
Спочатку генератор коду аналізує список ідентифікаторів і формує їхнє оголошення в сегменті даних (блоки 4, 6, 8). Далі відбувається подібна генерація машинного коду для списку стрічок (блоки 3, 5, 7). Після цього генератор формує заголовок машинного коду (блок 9). Далі зчитуються списки з проміжним представленням команд і формуються еквівалентні машинні інструкції (блоки 11, 13, 15). Далі в готовий асемблерний текст додаються сформовані машинні команди і код необхідних процедур (блоки 10, 12). Результати зберігаються у вихідний файл (блок 14).
Вихідна програма на машинній мові організована у вигляді готових процедур, кожна з яких відповідає за ввід/вивід результатів роботи, конкретну обчислювальну арифметичну чи логічну операцію, вивід повідомлення про помилки. Для обчислень використовується програмний стек, який представлений ділянкою пам’яті і зміщенням (вершина стеку). Даний метод має наступні переваги:
розмір стеку достатньо великий щоб уникнути помилок переповнення, як у випадку зі стеком математичного співпроцесора;
проста реалізація обчислень за допомогою готових процедур;
можливість виявлення помилок на етапі виконання:
ділення на нуль;
помилка ініціалізації змінної;
помилка переповнення;
помилка введення.
4. Опис інтерфейсу та інструкції користувача
Програма транслятор має простий інтерфейс SDI. Стандартне меню з командами збереження (Ctrl+S), відкриття (Ctrl+O), створення нового файлу (Ctrl+N). Також тут є меню з командами виконання всіх фаз трансляції і компіляції(F5). Меню довідки містить команду показу вікна з інформацією про програму. Головна робоча область складається з поля введення тексту і поля зі списком помилок.
/
Рис.8 Середовище розробки мови z45
///
Рис. 9. Користувацькі меню середовища розробки.
5. Відлагодження та тестування програми
Відлагодження програми відбувається в покроковому режимі в середовищі C++ Builder 2010 з використанням точок зупинки, що дає можливість знайти місце логічної помилки. В покроковому режимі у нас є можливість прослідкувати за значеннями змінних.
Тестування програми буде відбуватися з допомогою готових прикладів на вхідній мові в яких можуть використовуватися різноманітні оператори вхідної мови, можуть бути допущені різноманітні лексичні, граматичні та синтаксичні помилки.
5.1 Виявлення лексичних помилок
На етапі лексичного аналізу виникають наступні помилки: Нерозпізнана лексема – це помилка яка може виникнути на етапі лексичного аналізу, коли лексема відповідає регулярному виразу лексичної помилки. Завеликий числовий літерал – це помилка яка може виникнути на етапі лексичного аналізу, коли числовий літерал є завеликим для типу INTEGR32. Коли виникає будь-яка помилка, то вона записується в глобальний список помилок. Виявлені лексичні помилки виводяться в список помилок внизу робочої області (рис. 10).
// Приклад програми з лексичними помилкам на мові z45
program <lexerr>;
var
_r integer_2;
begin
scandddata ;
scandata(_r);
printdata(_r*_r);
end
/
Рис.10 вікно з повідомленням про помилки
5.2 Виявлення синтаксичних помилок
На етапі синтаксичного аналізу виявляється основна кількість помилок. Ці помилки пов’язані з невірними записами конструкцій вхідної мови, незавершеність програми, неправильним форматом виразів. Всі помилки виявленні на етапі синтаксичного аналізу заносяться в таблицю помилок.
Для початку протестуємо наступний код в якому навмисно допущено різноманітні помилки. Як видно з (рис. 11) транслятор коректно виявляє різноманітні синтаксичні помилки.
// Приклад програми з синтаксичними помилкам на мові z45
program <syntaxerr>;
var
_r integer_2;
begin
scandata _r);
printdata(_r*_r);
end
/
Рис.11
5.3 Виявлення семантичних помилок
Основною метою семантичного аналізу є перевірка типів операндів. Оскільки згідно варіанту присутній тільки один тип integer32, то така перевірка не буде проводитися. Виявлення семантичних помилок зводиться до перевірки того, чи змінні ініціалізовані початковими значеннями. Ця перевірка виконується на етапі виконання. Це досягається наявність додаткової інформації про ініціалізацію в сегменті коду. При виникненні такої помилки видасться повідомлення про помилку, виконання програми завершиться і вона поверне одиницю.
Синтаксично слідуючий код правильний, проте в ньму допущена семантична помилка: змінній _Х присвоюється неініціалізована змінна _tmp, тому на етапі виконання згенерується помилка (рис. 12).
Program <semerr>;
Var
_X, _tmp integer_2;
Begin
_X>>_tmp;
End
/
Рис.12
5.4. Загальна перевірка коректності роботи транслятора
Загальна перевірка коректності роботи транслятора полягає в транслюванні коректної вхідної програми з використанням всіх можливостей мови в асемблерний код та перевірці на правильність виконання програми, яку потрібно попередньо скомпілювати і транслювати за допомогою ml.exe
Приклад коретної програми:
program <correct>;
var
_ x, _y, _z integer_2;
begin
printdata(“Enter X”);
scandata(_x);
printdata(“Enter Y”);
scandata(_y);
for(_z>>1; _z<=10;_z>>_z+1;)
{
printdata(_z);
}
printdata(“X*Y”);
printdata(_x*_y);
end
Результат виконаня:
/
Висновки
В процесі виконання курсової роботи було виконано наступне: Складено формальний опис мови програмування z45, в термінах розширеної нотації Бекуса-Наура, виділено усі термінальні символи та ключові слова. Створено транслятор мови програмування z45, а саме:
Розроблено лексичний аналізатор, який виявляє лексичні помилки , та на виході дає таблицю лексем таблицю ідентифікаторів, з якими потім працює синтаксично-семантичний аналізатор. Розроблено синтаксичний та семантичний аналізатор, який виявляє синтаксичні та семантичні помилки на етапі аналізу та виконання, та на виході будує проміжне представлення, яке потім використовує генератор коду. Розроблено генератор коду, який генерує код, записаної нами програми на заданій мові програмування , у програму на мові асемблера . На виході ми отримуємо файл на мові асемблера , з якого ми отримаємо виконавчий файл, за допомогою ml i link.
Проведене тестування транслятора на тестових програмах за наступними пунктами:
виявлення лексичних помилок, виявлення синтаксичних помилок, загальна перевірка роботи компілятора.
В результаті виконання даної курсової роботи було успішно засвоєно методи розробки та реалізації компонент системного програмування.
Список літератури
Хантер Р. Проектирование и конструирование компиляторов. – М.: Финансы и статистика, 1984. – 232 с.
Ахо А., Ульман Дж. Теория синтаксического анализа, перевода и компиляции. – М.: Мир, 1978. – т.1, 612 с.
Серебряков В.А. Лекции по конструированию компиляторов. – М.: МГУ, 1993.
Н.Г.Голубь “Исскуство программирования на ассемблере. Лекции и упражнения”. –Киев “DiaSoft”, 2002 – 642с.
Л.Бэк “Введение в системное програмирование”. – М.: Мир, 1999
Грис Д. Конструирование компиляторов для цифровых вычислительных машин. - М.: Мир. 1975. - 554 с.
Шильдт Г. С#. – Санкт-Петербург: BXV, 2007. – 688 с.
Додаток A: Лістинг програми транслятора
// ---------------------------------------------------------------------------
#ifndef BetH
#define BetH
// ---------------------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include <ActnCtrls.hpp>
#include <ActnList.hpp>
#include <ActnMan.hpp>
#include <ComCtrls.hpp>
#include <ExtCtrls.hpp>
#include <Menus.hpp>
#include <StdActns.hpp>
#include <ToolWin.hpp>
#include <XPStyleActnCtrls.hpp>
#include <ComCtrls.hpp>
#include <Menus.hpp>
#include <ToolWin.hpp>
#include <ExtCtrls.