МІНІСТЕРСТВО ОСВІТИ І НАУКИ, МОЛОДІ ТА СПОРТУ УКРАЇНИ
НАЦІОНАЛЬНИЙ УНІВЕРСИТЕТ “ЛЬВІВСЬКА ПОЛІТЕХНІКА”
ІКТА
Пояснювальна записка
до курсової роботи
з дисципліни:
"Системне програмування" (III курс, 5-й семестр)
На тему : «Розробка системних програмних модулів
та компонент систем програмування»
Анотація
В даній курсовій роботі здійснено розробку транслятора з вхідної мови програмування, заданої завданням, на мову асемблер, з подальшою компіляцією отриманого коду і створенням виконавчого файлу. Даний транслятор виконуває лексичний аналіз, синтаксичний і семантичний і при наявності помилок створюває список помилок і попереджень. У курсовій роботі створений лексичний аналізатор на базі скінченного автомата, а також синтаксичний аналізатор на основі рекурсивного спуску.
Транслятор був розроблений за допомогою технології C# в середовищі MS Visual Studio 2010.
Анотація…………………………………………………………………………………..2
Зміст……………………………………………………………………………………….3
Завдання на курсову роботу…………………………………………………………....4
Вступ……………………………………………………………………………………....5
Огляд методів та способів проектування трансляторів……………………...........6
Формальний опис вхідної мови програмування……………………………............9
Деталізований опис вхідної мови в термінах розширеної нотації
Бекуса-Наура……………………………………………………………………………..9
Опис термінальних символів та ключових слів…………………………………….....11
Розробка транслятора вхідної мови програмування……………..........................13
Вибір технології програмування…………………………………………………….....13
Проектування таблиць транслятора та вибір структур даних ..............................15
Розробка лексичного аналізатора………………………………………………….....19
Розробка граф-схеми алгоритму ...................................................................................20
Опис програми реалізації лексичного аналізатора…………………...........................21
Розробка синтаксичного та семантичного аналізатора………………………........23
Розробка дерев граматичного розбору…………………………………......................23
Розробка граф-схеми алгоритму…………………………………………....................23
Опис програми реалізації синтаксичного та
семантичного аналізатора…………………………………………………………......25
Розробка генератора коду………………………………………..……………….......27
Розробка граф-схеми алгоритму…………………………………………....................27
Опис програми реалізації генератора коду…………………………….......................29
Опис інтерфейсу та інструкції користувача…………………………………..........30
Відлагодження та тестування програми……………………………………….......31
Виявлення лексичних помилок………………………………………………………......31
Виявлення синтаксичних помилок……………………………………………………...32
Виявлення семантичних помилок …………………………………………...……........33
Загальна перевірка коректності роботи транслятора .............................................34
Висновки………………………………………………………………………………..35
Список літератури………………………………………………………………........36
Додатки………………………………………………………………………………....37
А. Лістинг програм ........................................................................................................37
Завдання на курсову роботу
Тема. Розробка транслятора з вхідної мови програмування.
- типи даних: Integer16_t, Boolean, const string;
- оператор вводу: Get;
- оператор виводу: Put;
- блок тіла програми: Body, End
- оператор: If Then Goto Goto (Бейсік);
- регістр ключових слів: Up –low перший символ Up;
- регістр ідентифікаторів: Up –low8 перший символ _;
- операції арифметичні: ++, --, **, Div,Mod;
- операції порівняння: Eg, Ne, <<, >>
- операції логічні: !!, &&, ||;
- коментар: ##...##
- ідентифікатори змінних, числові константи, рядкові константи;
- оператор присвоєння: :=;
Для отримання виконавчого файлу з вихідного асемблерного коду потрібно використовувати ml.exe (MASM32) вбудований в MS Visual Studio 2010.
Вступ
Мовний процесор типу транслятор (транслятор) – це програмний комплекс, котрий на вході отримує текст програми на вхідній мові, а на виході видає версію програми на вихідній мові, що називається об‘єктною мовою. В більшості випадків як об‘єктна мова виступає мова команд деякої обчислювальної машини. Серед трансляторів можна виділити дві програмні системи:
компілятори – транслятори з мов програмування високого рівня;
асемблери – транслятори машинно-орієнтованих мов програмування.Перші транслатори з'явилися на початку 50-х років. З тих пір теорія і техніка побудови компіляторів істотно розвилися.
Процес компіляції як правило складається з декількох етапів: лексичного, синтаксичного та семантичного аналізів, генерації проміжного коду, оптимізації та генерації результуючого машинного коду. Крім цього, програма як правило залежить від сервісів, наданих операційною системою і сторонніми бібліотеками (наприклад, файловий ввід-висновок або графічний інтерфейс), і машинний код програми необхідно пов'язати з цими сервісами. Для зв'язування зі статичними бібліотеками виконується редактор зв'язків або компонувальник (який може представляти із себе окрему програму або бути частиною компілятора), а з операційною системою і динамічними бібліотеками зв'язування виконується на початку виконання програми завантажувача.
1. Огляд методів та способів проектування трансляторів
В даній курсовій роботі згідно із завданням для парних варіантів необхідно реалізувати нисхідний метод граматичного розбору. Низхідний розбір — один з методів визначення приналежності вхідного рядка деякій формальній мові, описаній LL(k)-граматикою.
Для кожного нетермінального символу K будується функція, яка для будь-якого вхідного слова x робить дві речі:
Знаходить найбільший початок z слова x, здатний бути початком виводжуваного з K слова
Визначає, чи є початок z виводжуваним з K
Така функція має задовольняти наступні критерії:
зчитувати із ще необробленого вхідного потоку максимальний початок A, який є початком деякого слова, виводжуваного з K
визначати чи є A вивідним з K або просто невивідним початком виводжуваного з K слова
У випадку, якщо такий початок зчитати не вдається (і коректність функції для нетермінала K доведена), тоді вхідні дані не відповідають мові, і потрібно зупинити розбір.
Розбір містить у собі виклики описаних вище функцій. Якщо для зчитаного нетермінала є складене правило, тоді при його розборі будуть викликані інші функції для розбору терміналів, що входять в нього. Дерево викликів, починаючи із самої«верхньої» функції еквівалентно дереву розбору.
LL(k)-граматики — це клас контекстно-вільних граматик з додатковими обмеженнями, а саме:
КВ-граматика називається LL(k)-граматикою, для деякого фіксованого k, якщо дія двох лівосторонніх виводів:
,
для яких з Firstk(x) = Firstk(y) випливає що α = β ().
Неформально, граматика G буде LL(k)-граматикою, якщо для ланцюжка k перших символів (за умови, що вони існують) решти непроаналізованого ланцюжка визначають, що з Aω2 існує не більше однієї альтернативи виведення ланцюжка, що починається з ω та продовжується наступними k термінальними символами.
Означення:
Властивості LL(k)-граматик
Не існує алгоритму, який перевіряє належність КВ-граматики класу LL(k) — граматик.
Існує алгоритм, який для конкретного k перевіряє, чи є задана граматика LL(k) — граматикою.
Якщо граматика є LL(k)-граматикою, то вона є LL(k+p)-граматикою, (p>0).
Клас LL(k)-граматик — це підклас КВ-граматик, який не покриває його.
На практиці найчастіше користуються найвужчим класом LL(1), і до недавнього часу взагалі вважалось, що побудувати аналізатор для LL(k) граматики, де k>1 практично неможливо.
Використання мов високого рівня надає можливість описувати програми для комп'ютера, використовуючи загальноприйняті позначення операцій і функцій.Та програми, що написані на мовах програмування високого рівня (алгоритмічних мовах програмування), комп'ютер "не розуміє". Для того, щоб він міг виконати програму, її потрібно перекласти на машинну мову. Для такого перекладу використовують спеціальні програми, що мають назву - транслятори.Транслятор - це програма, що призначена для перекладу тексту програми з однієї мови програмування на іншу. Процес перекладання називається трансляцією.
Розрізняють два типи трансляторів:
компілятори - інтерпретатори.
Компілятор - це програма, призначена для перекладу в машинні коди програми, що написана мовою високого рівня. Процес такого перекладання називається компіляцією.Кінцевим результатом роботи компілятора є програма в машинних кодах, яка потім виконується ЕОМ. Скомпільований варіант програми можна зберігати на дискові. Для повторного виконання програми компілятор вже не потрібен. Досить завантажити з диска в пам'ять комп'ютера скомпільований перед цим варіант і виконати його.
2. Формальний опис вхідної мови програмування
2.1. Деталізований опис вхідної мови в термінах розширеної нотації Бекуса-Наура.
<program> ::= Name <name> Data <VAR_blok> Body <code_blok> End .
<VAR_blok> ::=<declarations> [{;<declarations>} ];.
<declarations>::= Integer16_t <declaration_i> [{,<declaration_i>}] | Boolean <declaration_b> [{,<declaration_b>}] .
<declaration_i> ::= <ident> [ := <const_i>] .
<declaration_b> ::= <ident> [ := <const_b>] .
<ident> ::= _[<l _or_n>] .
<l_or_n> ::= <letter>|<number> .
<letter>::=<letter_l>|<letter_h>
<letter_l>::=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 .
<letter_h>::=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 .
<const_i> ::= [-]<number>[{<number>}] .
<const_b> ::=True|False
<code_blok> ::= <statement> [{<statement>}] .
<statement> ::= <equation>|<if>|<read>|<write><goto> .
<equation> ::= <ident> << <expression_i>|<expression_b>; .
<expression_i>::= <term> [{++ <term> | -- <term>}].
<term>::=<ident>|<const_i>|<factor>.
<factor>::=[{** <term> | Div <term>| Mod <term> | <brackets>}].
<brackets>::=(<expression_i>).
<expression_b>::= <term_b> [{Eg <term_b> | Ne <term_b> | << <term_b>| >> <term_b>}].
<term_b>::=<ident>|<const_b>|<factor_b>.
<factor_b>::=<term_b> [{ ||<term_b> | <and> |<not>|<brackets_b>}].
<and>::= [{ &&<term_b>}]
<not>::=!!<factor_b>
<brackets_b>::=(<expression_b>).
<if> ::= If (<expression_b>|<ident>) Then <code_blok>[Else <code_blok>] End If
<read> ::= Get(<ident>); .
<write> ::= Put(<expression_i>|<string>); .
<goto>::=Goto <label>
<label>::= {<number>}0
<string> ::= “<l_or_n>[{<l_or_n>}]” .
2.2. Термінальні символи та ключові слова.
Name – початок тексту програми, наступним описується ім’я програми;
Data - блок опису змінних
Body - початок блоку
End – кінець тіла програми (циклу);
Put– оператор виводу (змінних і рядкових констант)
Get – оператор вводу змінних;
:= - оператор присвоєння;
If – початок умовного оператора
Then – початок тіла умовного оператора
Goto – безумовний перехід
++ – операція додавання
-- – операція віднімання
** – операція множення
Div – операція ділення
Mov – операція знаходження залишку від ділення;
Eg – операція перевірки на рівність;
Ne – перевірка на нерівність;
<< – перевірка чи менше;
>> – перевірка чи більше/рівно;
!! – операція логічного заперечення;
&& – кон’юнкція;
|| – диз’юнкція;
Integer16_t – 16ти розрядні знакові цілі;
Boolean – однобайтні логічні змінні;
## – початок коментаря
## - завершення коментаря
“ – початок/завершення рядкової константи при операції виводу;
,– розділювач між деклараціями змінних;
; – ознака кінця оператора;
(– відкриваюча дужка;
) – закриваюча дужка;
Як термінальні символи використовуються також усі арабські цифри (0–9), латинські букви (a-z, A-Z), символи табуляції, символ переходу на нову стрічку, пробіл.
3. Розробка транслятора вхідної мови програмування
3.1. Вибір технології програмування.
Для розробки транслятора вхідної мови програмування було вибрано обєктно орієнтовану технологію програмування під платформу .NET.
Використання технології ООП полягає у розробці окремих, не пов'язаних між собою класів і використанні їх як необхідних програмісту базових типів даних, відсутніх у мові. При цьому загальна структура програми залишається традиційною. Об'єктно-орієнтоване програмування (ООП) це сукупність понять (клас, об'єкт, інкапсуляція, поліморфізм, наслідування), прийомів їх використання при проектуванні програм, а С# інструмент цієї технології. Суворе дотримання технології ООП припускає, що будь-яка функція в програмі представляє собою метод для об'єкта деякого класу. Це не означає, що потрібно вводити в програму які потрапило класи заради того, щоб написати необхідні для роботи функції. Навпаки, клас повинен формуватися в програмі природним чином, як тільки в ній виникає необхідність опису нових фізичних предметів або абстрактних понять (об'єктів програмування). З іншого боку, кожен новий крок у розробці алгоритму також повинен представляти собою розробку нового класу на основі вже існуючих. Врешті-решт уся програма в такому вигляді являє собою об'єкт деякого класу з єдиним методом run (виконати). Саме цей перехід (а не поняття класу і об'єкту, як такі) створює психологічний бар'єр перед програмістом, яке освоює технологію ООП. Програмування "від класу до класу" включає в себе ряд нових понять.
Перш за все, це - інкапсуляція даних, тобто логічне зв'язування даних з конкретною операцією. Інкапсуляція даних означає, що дані є не глобальними - доступними всій програмі, а локальними - доступними тільки малої її частини. Інкапсуляція автоматично має на увазі захист даних. Для цього в структурі class використовується специфікатор розділу private, що містить дані і методи, доступні тільки для самого класу. Якщо дані і методи містяться в розділі public, вони доступні ззовні класу. Розділ protected містить дані і методи, доступні з класу і будь-якого його похідного класу. Наявність останніх дозволяє говорити про ієрархію класів, де є класи - батьки - шаблони для створення класів - нащадків. Об'єкти, отримані з опису класу, називають екземплярами цього класу.
Другим по значущості поняттям є наслідування. Новий, або похідний клас може бути визначений на основі вже наявного, або базового. При цьому новий клас зберігає всі властивості старого: дані об'єкта базового класу включаються в дані об'єкта похідного, а методи базового класу можуть бути викликані для об'єкта похідного класу, причому вони будуть виконуватися над даними включеного в нього об'єкта базового класу. Інакше кажучи, новий клас успадковує як дані старого класу, так і методи їх обробки. Якщо об'єкт успадковує свої властивості від одного батька, то говорять про одиночному успадкування. Якщо ж об'єкт успадковує атрибути від декількох базових класів, то говорять про множинне успадкування. Третім за значимістю поняттям є поліморфізм. Він грунтується на можливості включення в дані об'єкта також і інформації про методи їх обробки (у вигляді вказівників на функції). Принципово важливо, що такий об'єкт стає "самодостатнім". Будучи доступним в деякій точці програми, навіть при відсутності повної інформації про його тип, він завжди може коректно викликати властиві йому методи. Поліморфної називається функція, незалежно визначена в кожному з групи похідних класів і має у них спільне ім'я.
3.2. Проектування таблиць транслятора.
Для реалізації лексичного аналізу створюємо таблицю, в яку поміщаємо всі лексеми (ArrayList lexs = new ArrayList()), і таблицю (Hashtable idt = new Hashtable()) для ідентифікаторів, які будуть введені користувачем. Ім’я програми в цю таблицю не заноситься. Для реалізації таблиці лексем описаний тнаступний клас Lex:
Id
lex
сom
typeLex
зберігання номера лексеми
зберігання лексеми
зберігання класу до якого належить лексема
зберігання коду лексеми
public class Lexem
{
private int id=0;
private string lex = "";
private string com = "";
private int typeLex = 0;
public int Id
{ get { return id; }
set { id = value; } }
public int TypeLex
{ get { return type; }
set { type = value; } }
public string Lex
{ get { return lex; }
set { lex = value; }}
public string Com
{ get { return com; }
set { com = value; }}
}
Відомості про класи з таблиці лексем використовуються при синтаксичному аналізі.
Лексеми мають наступні атрибути:
-----Type-----
Нерозпізнана лексема - -1
Ключові слова - 0
Ідентифікатори - 1
Числові константи - 2
Рядкові константи - 3
Коментарі - 4
Eg - 5
!! - 6
&& - 7
|| - 8
Ne - 9
:= - 10
; - 11
, - 12
) - 13
( - 14
---TypeKeyWords----
Name - 0
Data - 1
Body - 2
End - 3
Integer16_t - 4
Boolean - 5
Get - 6
Put - 7
++ - 8
-- - 9
True - 10
False - 11
If -12
Else - 13
** - 14
Div -15
Mod - 16
<< - 17
>> -18
Goto – 19
Атрибути використовуються генератором коду для формування відповідних процедур мовою асемблер.
Для зберігання таблиці ідентифікаторів використовується Hashtable idt = new Hashtable()
Id
type
Id_value
зберігання ідентифікаторів
зберігання типу змінної
зберігання значення змінної
public class Iden_info
{
private string id="";
private string type = "";
private string Id_value = "";
public string Id
{
get { return id; }
set { id = value; }
}
public string Type
{
get { return type; }
set { type = value; }
}
public string Value
{
get { return Id_value; }
set { Id_value = value; }
}
}
Для запам’ятовування виразу в постфікс ній формі використовується ArrayList.
3.3. Розробка лексичного аналізатора.
Лексичний аналіз - це процес перетворення послідовності символів в послідовність токенів (груп символів що відповідають певним шаблонам), та визначення їх типів. Програма, чи функція що виконує лексичний аналіз, називається лексичним аналізатором, чи сканером. Часто сканер є звичайною функцію що використовується парсером (синтаксичним аналізатором), для отримання наступного токена з потоку вхідних символів в процесі компіляції.
3.3.1. Розробка граф-схеми алгоритму
При розробці алгоритму роботи лексичного аналізатора використовується скінченний автомат. Особливостями цього алгоритму є те, що розбір виконується відносно кількості символів з яких складаються лексеми. Для кожного класу багатосимвольної лексеми розроблена функція, яка виділяє лексему, або констатує що дана лексема не відноситься до жодного з заданих класів лексем.
Рис 1.Граф-схема роботи лексичного аналізатора
Блок 1 – Початок роботи лексичного аналізатора
Блок 2 – Відкриття і порядкове читання вхідного файлу з текстом програми
Блок 3 – Перевірка чи не досягнутий кінець файлу
Блок 4 – Виділення наступного слова
Блок 5 – Перевіряє чи перший символ A-Z
Блок 6 – Розпізнанння і збереження ключових слів
Блок 7 – Перевіряє чи перший символ a-z
Блок 8 – Розпізнанння і збереження ідентифікаторів
Блок 9 – Перевіряє чи перший символ 0-9
Блок 10 – Розпізнанння і збереження числових констант
Блок 11 – Перевіряє чи перший символ “
Блок 12 – Розпізнанння і збереження рядкових констант
Блок 13 – Перевіряє чи символ є оператор
Блок 14 – Зберігання до таблиці лексем
Блок 15 – Перевіряє чи перший символ є $
Блок 16 – Розпізнанння і збереження коментарів
Блок 17 – Перевіряє чи символ є розділювач
Блок 18 – Зберігання до таблиці лексем
Блок 19 – Якщо перший символ не є жодним з вищеперечислених
Блок 20 – Збереження всіх інших лексем
Блок 21 – Якщо файл прочитано, завершення роботи лексичного аналізатора
3.3.2Опис програми реалізації лексичного аналізатора
Програма по рядках читає вхідний файл. Прочитаний рядок передає як параметр функції Parse класу Parser. В лексичному аналізаторі для розпізнання використовуються наступні функції:
Функція Rozbir() аналізує перший символ нової лексеми:
якщо перший символ A-Z, то запускається функція KeyWords()
якщо перший символ 0-9 або -, то запускається функція Num()
якщо перший символ <<,>>,:=,!,&,|,;,++,--,(,),** відбувається збереження лексеми
якщо перший символ ##, то запускається функція Comments()
якщо перший символ “,то запускається функція String()
якщо перший символ не є жодним з вищезгаданих то запускається процедура NoLex()
Функція KeyWords() виділяє ключові слово
Функція Ident()виділяє ідентифікатор
Функція Num()виділяє числові константи і зберігає лексему
Функція Comments() виділяє коментарі
Функція String() шукає символ завершення рядка і всі символи між початком і кінцем рядка вважає рядковою константою
Функція NoLex() шукає розділювач і вважає що всі символи, які вона виділила є нерозпізнаною лексемою
3.4.Розробка синтаксичного аналізатора
Синтаксичний являється першим етапом синтаксичного аналізу. При його виконанні здійснюється підтвердження того, що вхідний ланцюжок символів є програмою, а окремі підланцюжки складають синтаксично правильні програмні об'єкти. Розбір призначений для доказу того, що аналізований вхідний ланцюжок, записаний на вхідній стрічці, належить або не належить безлічі ланцюжків породжуваних граматикою даної мови. Виконання синтаксичного розбору здійснюється розпізнавачами, тому даний процес також називається розпізнаванням вхідного ланцюжка.
Основним завданням семантичного аналізатора є перевірка типів. Також семантичний аналізатор повинен знаходити вирази, що використовуються без присвоєння та видавати попередження.
3.4.1. Розробка дерева граматичного розбору
Дерево граматичного розбору розроблено згідно правил, даних у формі розширеної нотації Бекуса-Наура, та оформлено згідно правил ЄСКД.
3.4.2 Розробка граф-схеми алгоритму
Рис 2.Граф-схема роботи синтаксичного аналізатора
Блок 1 – Початок синтаксичного аналізатора
Блок 2 – Основна функція аналізатора
Блок 3 – Перевіряє чи наступний символ Data
Блок 4 – Виконання функції ParseData()
Блок 5 - Перевіряє чи наступний символ Integer16_t
Блок 6 - Виконання функції ParseDeclar_i()
Блок 7 - Виконання функції ParseData()
Блок 8 - Перевіряє чи наступний символ Boolean
Блок 9 - Виконання функції ParseDeclar_b()
Блок 10 - Виконання функції ParseData()
Блок 11 - Перевіряє чи наступний символ Body
Блок 12 - Перевіряє чи наступний символ Put
Блок 13 - Виконання функції ParsePut()
Блок 14 - Виконання функції ParseBody()
Блок 15 - Перевіряє чи наступний символ Get
Блок 16 - Виконання функції ParseGet()
Блок 17 - Виконання функції ParseBody()
Блок 18 - Перевіряє чи наступний символ <ident>
Блок 19 - Виконання функції ParseEqution()
Блок 20 - Виконання функції ParseBody()
Блок 21 - Перевіряє чи наступний символ If
Блок 22 - Виконання функції ParseIF()
Блок 23 - Виконання функції ParseBody()
Блок 24 – Якщо інший символ?
Блок 25 – Функція Error();
Блок 26 - Виконання функції ParseBody()
Блок 27 - Перевіряє чи наступний символ End
Блок 28 – Якщо не фініш виконання функції Error();
Блок 29 - Завершення Синтаксичного аналізатора
3.4.3 Опис програми реалізації синтаксичного та семантичного аналізатора
На вхід синтаксичного аналізатора подається таблиця лексем, створена на етапі лексичного аналізу. Потім по черзі перебираємо лексеми та аналізуємо класи лексем, що слідують за ними. Якщо після чергової лексеми слідує лексема, що має некоректний клас (наприклад після лексеми класу вводу/ виводу слідує лексема класу математичних операторів), то формується повідомлення про помилку. Також здійснюється перевірка чи не йдуть підряд дві лексеми однакового класу (за винятком синтаксичних та операції множення на від’ємне число), якщо це справджується то виводиться повідомлення про помилку. При знаходженні оператора присвоєння, математичних та логічних операторів чи операторів порівняння здійснюється перевірка атрибутів усіх ідентифікаторів, що входять у даний вираз. Якщо у виразі використовуються дані різних типів, то формується повідомлення про помилку.
При кожному знаходженні помилки в таблицю помилок додається одне поле.
3.5.Розробка генератора коду
3.5.1 Розробка граф-схеми алгоритму
Так як генерація коду тісно пов’язана з синтаксичним аналізом, тобто код конструкції генерується відразу після розпізнавання.
Рис 3.Граф-схема роботи генератора коду
Блок 1 – Початок синтаксичного аналізатора
Блок 2 – Основна функція аналізатора
Блок 3 – Перевіряє чи наступний символ Data
Блок 4 – Генерація блоку даних
Блок 5 - Перевіряє чи наступний символ Integer16_t
Блок 6 – Генерація 4-х байтних змінних
Блок 7 - Виконання функції ParseData()
Блок 8 - Перевіряє чи наступний символ Boolean
Блок 9 – Генерація однобайтних змінних
Блок 10 - Виконання функції ParseData()
Блок 11 - Перевіряє чи наступний символ start
Блок 12 - Перевіряє чи наступний символ Put
Блок 13 – Генерація процедури виводу
Блок 14 - Виконання функції ParseBody()
Блок 15 - Перевіряє чи наступний символ Get
Блок 16 - Генерація процедури вводу
Блок 17 - Виконання функції ParseBody()
Блок 18 - Перевіряє чи наступний символ <ident>
Блок 19 - Генерація виразів
Блок 20 - Виконання функції ParseBody()
Блок 21 - Перевіряє чи наступний символ If
Блок 22 – Генерація блоку If
Блок 23 - Виконання функції ParseBody()
Блок 24 – Перевіряє чи наступний символ End
Блок 25 – Генерація завершення програми
Блок 26 - Завершення Синтаксичного аналізатора
3.5.2 Опис програми реалізації генератора коду
Програма починає по черзі перебирати лексеми із таблиці лексем. У відповідності до коду знайденої лексеми у проміжне представлення програми буде вставлено відповідний еквівалентний асемблерний текст. Наприклад, при зустрічі ключового слова Program, в асемблерний файл вставляється текст з описом моделі та сегментів, а при зустрічі ключового слова Var, вставляється опис сегменту даних.
Реакція на лексеми розроблена так, що пустий код не буде включений в асемблерний файл. Для того щоб уникнути помилок, імена ідентифікаторів, дані у вхідній програмі користувачем, вносяться у асемблерний файл із змінами. Наприклад, невідомо, як буде працювати згенерований код, якщо у ньому будуть зустрічатись створені користувачем змінні end, loop.
В даному трансляторі генератор коду послідовно викликає окремі функції, які записують у вихідний файл частини коду. Для кожного ланцюжка вхідної мови існує окрема функція, яка враховуючи послідовність лексем створює відповідний вихідний код.
4. Опис інтерфейсу та інструкції користувачеві
Програма G60 можна запустити з виконавчого файлу G60.exe. G60 є програмою, яка працює на платформі .NET і є візуальною програмою, тому для її запуску повинна бути встановлена платформа net framework не нижче 2 версії. G60 містить редактор тексту і транслятор. Редактор тексту дозволяє відкривати файли з розширенням *.v60.
Рис 4.Вікно редактора тексту
В редакторі присутні наступні функції:
Ведення і редагування тексту
Відкриття нового файл
Збереження файлу
Закриття файлу
Копіювання тексту
Запустити транслятор вхідної мови можна за допомогою пункту меню Build/Run (F5). Вікно транслятора складається з 5 вкладок:
Таблиця лексем
Таблиця Ідентифікаторів
Таблиця рядкових констант
Таблиця помилок
Файл Асемблерного коду
Рис 6.Вікно транслятора
Щоб отримати виконавчий файл потрібно згенерований код скомпілювати за допомогою ml.exe який вбудований в MS Visual Studio 2010.
5. ВІДЛАГОДЖЕННЯ ТА ТЕСТУВАННЯ ПРОГРАМИ
5.1. Виявлення лексичних помилок.
До помилок виявлених на етапі лексичного аналізу відносить тільки одна помилка – виявлення нерозпізнаної лексеми. Якщо було виявлено нерозпізнану лексему – в таблицю лексем заноситься поле з коментарем «нерозпізнана лексема», і їй присвоюється код -1, і на етапі синтаксичного аналізу буде згенерована помилка:
Рис 7.Виявлення помилок на етапі лексичного аналізу
5.2. Виявлення синтаксичних помилок.
На етапі синтаксичного аналізу виявляється основна кількість помилок. Ці помилки пов’язані з невірними записами конструкцій вхідної мови. Всі помилки виявленні на етапі синтаксичного аналізу заносяться в таблицю помилок, Таблиця помилок містить лексему яка спричинила помилку, коментар і рядок в якому виникла помилка. Приклад таблиці помилок наведений на рис. 8:
Рис 8.Виявлення помилок на етапі синтаксичного аналізу
5.3. Виявлення семантичних помилок.
Суттю виявлення семантичних помилок є перевірка числових констант на відповідність типу Integer16_t , тобто знаковому цілому числу з відповідним діапазоном значень і перевірку на коректність використання змінних Integer16_t і Boolean у цілочисельних і логічних виразах.
5.4. Загальна перевірка коректності роботи транслятора.
Загальна перевірка полягає в транслюванні завідомо коректної вхідної програми з використанням всіх можливостей мови в асемблерний код та перевірці на правильність виконання програми попередньо скомпільованої та злінкованої за допомогою ml.exe
Текст програми:
Name _prog1
Data Integer16_t _a:=0, _b:=0;
Integer16_t _c:=0;
Boolean _b1:=True;
Body
Put("a = ");
Get(_a);
Put("b = ");
Get(_b);
If (_a Ne _b) Then
_a := _a ++ 1;
Put("a = ");
Put(_a);
Goto 100
Else
_a := _a ** _b;
Put("a = ");
Put(_a);
End If
End
Результати роботи програми:
Висновки
Підчас виконання курсової роботи:
1. Складено формальний опис мови програмування G60 у формі розширеної нотації Бекуса-Наура, дано опис усіх символів та ключових слів.
2. Створено транслятор мови програмування G60, а саме:
2.1.1. Розроблено лексичний аналізатор, здатний розпізнавати лексеми, що є описані в формальному описі мови програмування, та додані під час безпосереднього використання транслятора.
2.1.2. Розроблено синтаксичний аналізатор на основі рекурсивного спуску. Дерево виклику функцій згідно правил записаних в нотації у формі Бекуса-Наура.
2.1.3. Розроблено генератор коду, який починає свою роботу під час коректного виявлення кожної конструкції і генерує код який відповідає кожній конструкції вхідної мови. Проміжним кодом генератора є програма на мові Assembler(i586). Вихідним кодом є машинний код, що міститься у виконуваному файлі
3. Проведене тестування транслятора за допомогою тестових програм за наступними пунктами: Виявлення лексичних помилок. Виявлення синтаксичних помилок. Загальна перевірка роботи компілятора. Тестування не виявило помилок в роботі компілятора, а всі помилки в тестових програмах мовою G60 були виявлені і дано попередження про їх наявність. В результаті виконання даної курсової роботи було успішно засвоєно методи розробки та реалізації компонент системного програмного забезпечення. Список використаної літератури
Ахо и др. Компиляторы: принципы, технологии и инструменты.: Пер с англ. – М.: Издательський дом «Вильямс». 2003. – 768 с.: ил. Парал. тит. англ.
Шильдт Г. С#. – Санкт-Петербург: BXV, 2002. – 688 с.
Компаниец Р.И., Маньков Е.В., Филатов Н.Е. Системное программирование. Основы построения трансляторов. – СПб.: КОРОНА принт, 2004. – 256 с.
Б. Керниган, Д. Ритчи «Язык программирования Си». – Москва «Финансы и статистика», 1992. – 271 с.
Л. Дао. Программирование микропроцессора 8088. Пер.с англ.М. «Мир», 1988.
Ваймгартен Ф. Трансляция языков программирования. – М.: Мир, 1977.
Додаток А
Лістинг програми:
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Collections;
namespace Notepad
{
public class Const_Strings
{
private string str_value = "";
private int num_lex;
public string Value
{
get { return str_value; }
set { str_value = value; }
}
public int Number
{
get { return num_lex; }
set { num_lex = value; }
}
}
public class Iden_info
{
private string id="";
private string type = "";
private string Id_value = "";
public string Id
{
get { return id; }
set { id = value; }
}
public string Type