Кафедра ЕОМ
/
Пояснювальна записка
до курсової роботи
з дисципліни:
"Системне програмування" (III курс, 5-й семестр)
на тему : «Розробка системних програмних модулів та компонент систем програмування»
Анотація
В даній курсовій роботі розроблено транслятор з вхідної мови програмування на мову асемблер, який здійснює компіляцію отриманого коду і створює виконавчий файл. Розроблений транслятор виконує лексичний аналіз,синтаксичний і семантичний, і при виявлення помилок створює список помилок. Тобто дана програма здійснює перевірку вхідного файлу на помилки (синтаксичні, семантичні, лексичні).
Лексичний аналізатор був розроблений на базі скінченного автомата,а синтаксичний аналізатор на основі рекурсивного спуску.
Дана курсова робота виконана у середовищі Microsoft Visual Studio 2010.
Зміст
Анотація……………………………………………………………………………………2
Зміст…………………………………………………………………………..……………3
Завдання на курсову роботу…………………………………………………...………..4
Вступ………………………………………………………………………………………5
Огляд методів та способів проектування трансляторів………………………….6
Формальний опис вхідної мови програмування………………………………….8
Деталізований опис вхідної мови в термінах розширеної нотації
Бекуса-Наура……………………………………………………………………………..8
Опис термінальних символів та ключових слів………………………………………9
Розробка транслятора вхідної мови програмування…………………………….11
Вибір технології програмування………………………………………………………..11
Проектування таблиць транслятора та вибір структур даних……………………12
Розробка лексичного аналізатора………………………………….…………………..15
Розробка граф-схеми алгоритму………………………………….…………………..16
Опис програми реалізації лексичного аналізатора………………………..….……17
Розробка синтаксичного та семантичного аналізатора………………………..….19
Розробка граф-схеми алгоритму………………………………………………….…..20
Опис програми реалізації синтаксичного та
семантичного аналізатора…………………..…………………………………………………21
Розробка генератора коду……………………………………………………………….23
Розробка граф-схеми алгоритму………………..…………………………….…23
Опис програми реалізації генератора коду………………..……………………….25
Опис інтерфейсу та інструкції користувача……………………………………...26
Відлагодження та тестування програми…………………………………………27
Виявлення лексичних помилок………………………………………………………….27
Виявлення синтаксичних помилок…………………………………………………….27
Виявлення семантичних помилок……………………………………………………..27
Загальна перевірка коректності роботи транслятора………………………………28
Висновки………………………………………………………………………………….29
Список літератури……………………………………………………………………....30
Додатки…………………………………………………………………………………...31
А. Лістинг програм……………………………………………….………………………………31
Завдання на курсову роботу
Темою даного курсового проекту є розробка системних програмних модулів та компонент систем програмування.
Варіант №30
Опис вхідної мови:
Блок тіла програми : Name <name>; Data…;Body - End
Оператор вводу : Read
Оператор виводу : Write
Оператор присвоєння : ->
Оператор: Do – While (CI)
Регістр ключових слів : Up-Low перший символ Up
Регістр ідентифікаторів : Up-Low8 перший символ _
Арифметичні операції : ++; --; **; Div; Mod
Операції порівняння : =; <>;= >; <=
Логічні операції : !!; &&; ||
Типи даних : Longint,const_string
Коментар : \\...
Вступ
Під системним програмуванням розуміють роботу, яка полягає з системним програмним забезпеченням.
Транслятором являється спеціальна програма, яка використовується для перекладу програм користувача, написаних мовою програмування високого рівня, у так звані машинні коди. Транслятор зазвичай виконує також діагностику помилок, формує словники ідентифікаторів і т. д.
Роблячи висновки можна зрозуміти, що транслятори перекладають команди користувача в набір команд процесора. Це дає можливість використовувати програми, які були створені мовою низького рівня на різних типах машин.
Мова, на якій представлена вхідна програма, називається вихідним мовою, а сама програма – вихідним кодом. Вихідна мова називається цільовою мовою або об'єктним кодом.
Розрізняють два типи трансляторів:компілятори, інтерпретатори.
Компілятор - це програма, призначена для перекладу в машинні коди програми, що написана мовою високого рівня. Процес такого перекладання називається компіляцією. Кінцевим результатом роботи компілятора є програма в машинних кодах, яка потім виконується ЕОМ. Інтерпретатор - це програма, що призначена для трансляції та виконання вихідної програми по командах. Такий процес називається інтерпретацією.
У процесі трансляції відбувається перевірка програми на відповідність до правил її написання.
Зручніше було б подавати текст вхідної мови у вигляді послідовності лексем. У випадку відсутності помилок повинен генеруватися код.
1. Огляд методів та способів проектування трансляторів
Є такі методи створення компіляторів:
1. Прямий метод- цільовою мовою і мовою реалізації є асемблер.
2. Метод розкрути- саме цей метод і використовується у даній курсовій роботі, тобто вибирається інструмент (в даній курсовій це мова асемблер), для якого вже існує компілятор.
3.Використання крос-трансляторів.
4.З використанням віртуальних машин–дає спосіб отримати переносимо програму.
5. Компіляція на ходу.
В даній курсовій роботі згідно із завданням для парних варіантів необхідно реалізувати нисхідний метод граматичного розбору. Низхідний розбір — один з методів визначення приналежності вхідного рядка деякій формальній мові, описаній LL(k)-граматикою.
Для кожного нетермінального символу K будується функція, яка для будь-якого вхідного слова x робить дві речі:
Знаходить найбільший початок z слова x, здатний бути початком виводжуваного з K слова
Визначає, чи є початок z виводжуваним з K
Така функція має задовольняти наступні критерії:
зчитувати із ще необробленого вхідного потоку максимальний початок A, який є початком деякого слова, виводжуваного з K
визначати чи є A вивідним з K або просто невивідним початком виводжуваного з K слова
У випадку, якщо такий початок зчитати не вдається (і коректність функції для нетермінала K доведена), тоді вхідні дані не відповідають мові, і потрібно зупинити розбір.Розбір містить у собі виклики описаних вище функцій. Якщо для зчитаного нетермінала є складене правило, тоді при його розборі будуть викликані інші функції для розбору терміналів, що входять в нього. Дерево викликів, починаючи із самої«верхньої» функції еквівалентно дереву розбору.
Нисхідний метод полягає в побудові дерева розбору, починаючи від кореневої вершини. Розбір полягає в заповненні проміжку між початковим нетерміналом і символами вхідного ланцюжка правилами, виведеними з початкового нетермінала. Підстановка ґрунтується на тім факторі, що коренева вершина є вузлом, що складається з листів, що є ланцюжком терміналів і нетерміналів одного з альтернативних правил, породжуваних початковим нетерміналом. Правило, що підставляється, у загальному випадку обирається довільно. Замість нових нетермінальних вершин здійснюється підстановка виведених з них правил. Процес протікає доти, поки не будуть установлені всі зв'язки дерева, що з'єднують кореневу вершину і символи вхідного ланцюжка, чи поки не будуть перебрані всі можливі комбінації правил. В останньому випадку вхідний ланцюжок відкидається. Побудова дерева розбору підтверджує приналежність вхідного ланцюжка даній мові.
В даній курсовій роботі реалізовано метод рекурсивного спуску оскільки він є одним із найпростіших нисхідних методів. Для кожного правила граматики створюється окрема процедура. Перший символ із вхідної послідовності має одночасно ідентифікувати правило відповідно до процедури.
2. Формальний опис вхідної мови програмування
2.1. Деталізований опис вхідної мови в термінах розширеної нотації Бекуса-Наура.
<program>::=Name <ident>Data<data_blok>Body<code_blok>End.
<data_blok> ::=<declarations> [{;<declarations>} ];.
<declarations>::=Longint<declaration_i> [{,<declaration_i>}].
<declaration_i> ::= <ident> [-> <const_i>] .
<ident> ::= _[<l _or_n>].
<l_or_n> ::= <letter>|<number> .
<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|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}] .
<code_blok> ::= <statement> [{<statement>}] .
<statement> ::= <equation>|<cycle>|<read>|<write> .
<equation> ::= <ident> [-> <expression_i>]; .
<expression_i>::= <term> [{++ <term> | -- <term>}].
<term>::=<ident>|<const_i>|<factor>.
<factor>::=[{** <term> | Div <term>| Mod <term> | <brackets>}].
<brackets>::=(<expression_i>).
<expression_b>::= <term_b> [{== <term_b> | != <term_b> | le<term_b>| ge <term_b>}].
<term_b>::=<ident>|<const_i>|<factor_b>.
<factor_b>::=<term_b> [{ ||<term_b> | <and> |<not>|<brackets_b>}].
<and>::= [{ &&<term_b>}]
<not>::=!!<factor_b>
<brackets_b>::=(<expression_b>).
<cycle> ::= Do <code_blok> While (< ident>|<expression_b>).
<read> ::= Read(<ident>); .
<write> ::= Write(<expression_i>|<string>); .
<string> ::= “<l_or_n>[{<l_or_n>}]” .
2.2. Термінальні символи та ключові слова.
Name – початок тексту програми, наступним описується ім’я програми;
Data - блок опису змінних;
Body – початок тіла програми;
End – кінець тіла програми;
Write– оператор виводу (змінних і рядкових констант)
Read – оператор вводу змінних;
-> - оператор присвоєння;
While – початок умовного уиклу
Do – початок тіла умовного циклу
++ – операція додавання
-- – операція віднімання
** – операція множення
Div – операція ділення
Mod – операція знаходження залишку від ділення;
= – операція перевірки на рівність;
<> – перевірка на нерівність;
>= – перевірка чи більше рівно;
<= – перевірка чи менше рівно;
!! – операція логічного заперечення;
&& – кон’юнкція;
|| – диз’юнкція;
Longint– знакові цілі;
\\ – рядковий коментар
“ – початок/завершення рядкової константи при операції виводу;
,– розділювач між деклараціями змінних;
; – ознака кінця оператора;
(– відкриваюча дужка;
) – закриваюча дужка;
Також, як термінальні символи використовуються також усі арабські цифри (0–9), латинські букви (a-z, A-Z), символи табуляції, символ переходу на нову стрічку, пробіл.
3. Розробка транслятора вхідної мови програмування
3.1. Вибір технології програмування.
Для виконання курсової роботи була вибрана об’єктно-орієнтована технологія програмування. Обєктно орієнтоване програмування (надалі ООП) - парадигма програмування, в якій основними концепціями є поняття об'єктів і класів.
Структура даних "клас" є об'єктним типом даних. При цьому елементи такої структури (члени класу) можуть самі бути не лише даними, але і методами (тобто процедурами або функціями). Таке об'єднання називається інкапсуляцією.
Наявність інкапсуляції достатня для объектности мови програмування, але ще не означає його об'єктної орієнтованості - для цього потрібно наявність наслідування.
Але навіть наявність інкапсуляції і наслідування не робить мову програмування повною мірою об'єктною з точки зору ООП. Основні переваги ООП проявляються тільки у тому випадку, коли в мові програмування реалізований поліморфізм.
Абстракція - це надання об'єкту характеристик, які відрізняють його від усіх інших об'єктів, чітко визначаючи його концептуальні межі. Основна ідея полягає в тому, щоб відокремити спосіб використання складених об'єктів даних від деталей їх реалізації у вигляді простіших об'єктів, подібно до того, як функціональна абстракція розділяє спосіб використання функції і деталей її реалізації в термінах примітивніших функцій, таким чином, дані обробляються функцією високого рівня за допомогою виклику функцій низького рівня.
Такий підхід є основою об'єктно-орієнтованого програмування. Це дозволяє працювати з об'єктами, не вдаючись особливо їх реалізації. 3.2. Проектування таблиць транслятора.
Для реалізації лексичного аналізу потрібно створити таблицю, в яку помістимо всі лексеми (ArrayList leksems = new ArrayList()), і таблицю (Hashtable ident = new Hashtable()) для ідентифікаторів, які будуть введені користувачем. Ім’я програми в цю таблицю не заноситься. Для реалізації таблиці лексем описаний тнаступний клас Lexem:
Id_nmb
leks
koment
type
зберігання номера лексеми
зберігання лексеми
зберігання класу до якого належить лексема
зберігання коду лексеми
public class Lexem
{
private int id_nmb=0;
private string leks = "";
private string koment = "";
private int type = 0;
public int Id_nmb
{ get { return id_nmb; }
set { id_nmb = value; } }
public int Type
{ get { return type; }
set { type = value; } }
public string Leks
{ get { return leks; }
set { leks = value; }}
public string Koment
{ get { return koment; }
set { koment = value; }}
}
Лексеми мають наступні коди:
Нерозпізнана лексема - -1
Ключові слова - 0
Ідентифікатори - 1
Числові константи - 2
Рядкові константи - 3
Коментарі - 4
= - 5
!! - 6
&& - 7
|| - 8
<> - 9
-> - 10
; - 11
, - 12
) - 13
( - 14
-- - 15
++ - 16
** - 17
>= - 18
<= - 19
Ключові слова мають такі коди:
Name - 0
Data - 1
Body - 2
End - 3
Longint - 4
Read - 5
Write - 6
While -7
Do - 8
Div -9
Mod - 10
Атрибути використовуються генератором коду для формування відповідних процедур мовою асемблер.
Для зберігання таблиці ідентифікаторів використовується Hashtable identy = new Hashtable()
Полем таблиці є клас Iden_info він містить наступні поля:
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Опис програми реалізації лексичного аналізатора
Програма по рядках читає вхідний файл. Прочитаний рядок передає як параметр функції Rozbir класу Rozbir. В лексичному аналізаторі для розпізнання використовуються наступні функції:
Функція Parse() аналізує перший символ нової лексеми:
символ A-Z - то запускається функція ParseKeyWords()
символ a-z - то запускається функція ParseIdent()
cимвол 0-9 або -, - запускається функція ParseNum()
символ =,<,>,!,&,|,;, , ,(,) - відбувається збереження лексеми
символ \ - запускається функція ParseKoments()
символ “,то запускається функція ParseString()
символ не є жодним з вищезгаданих - запуск процедури ParseNoLeks()
Функція ParseKeyWords() виділяє послідовність символі, які можуть містити ключові слова і перевіряє чи ця послідовність символів є ключовим словом і якщо це ключове слово зберігає його, як лексему
Функція ParseIdent() виділяє послідовність символі, які можуть містити ідентифікатори і зберігає ідентифікатор
Функція ParseNum() виділяє послідовність символі, які можуть містити числові константи і зберігає лексему
Функція ParseComments() перевіряє чи 2 символ теж $, то всі символи до кінця рядка вважає коментарем
Функція ParseString() шукає символ завершення рядка і всі символи між початком і кінцем рядка вважає рядковою константою
Функція ParseNoLex() шукає розділювач і вважає що всі символи, які вона виділила є нерозпізнаною лексемою
3.4.Розробка синтаксичного аналізатора
Першим етапом синтакчиного аналізу є розпізнавання. Перевіряється чи вхідний ланцюжок є програмою. Після розпізнавання здійснюється семантичний аналіз. Після чого проводиться додавання нових об'єктів в об'єктну модель програми або в проміжне представлення.
Виконання синтаксичного розбору здійснюється розпізнавачами, тому даний процес також називається розпізнаванням вхідного ланцюжка. Мета доказу в тому, щоб відповісти на питання: чи належить аналізований ланцюжок безлічі правильних ланцюжків заданої мови. Відповідь «так» дається, якщо така приналежність встановлена. Інакше дається відповідь «ні». Отримання відповіді «ні» пов'язано з поняттям відмови. Єдина відмова на будь-якому рівні веде до загальної відмови.
Щоб одержати відповідь «так» щодо всього ланцюжка, треба його одержати для кожного правила, що забезпечує розбір окремого підланцюжка. Аналізатор вибирає перший символ з сукупності лексем аналізуючи його визначає, яку функцію треба запустити щоб виконати перевірку заданого підланцюжка. Цей метод називається методом рекурсивного спуску, аналіз проводиться від кореня до листків.
Основним завданням семантичного аналізатора є перевірка типів. Також семантичний аналізатор повинен знаходити вирази, що використовуються без присвоєння та видавати попередження.
Сама програма перевірки типів базується на інформації про синтаксичні конструкції мови, представлення типів і правилах присвоєння типів конструкціям мови.
3.4.1 Розробка граф-схеми алгоритму
Рис 2.Граф-схема роботи синтаксичного аналізатора
Блок 1 – Початок синтаксичного аналізатора
Блок 2 – Основна функція аналізатора
Блок 3 – Перевіряє чи наступна лексема Data
Блок 4 – Виконання функції розбору блоку оголошення змінних
Блок 5 - Перевіряє чи наступна лексема Longint
Блок 6 - Виконання функції розбору оголошення цілочисельних змінних
Блок 7 - Перевіряє чи наступна лексема Body
Блок 8 - Перевіряє чи наступна лексема Write
Блок 9 - Виконання функції розбору функції виводу
Блок 10 - Перевіряє чи наступна лексема Read
Блок 11 - Виконання функція розбору функції вводу
Блок 12 - Перевіряє чи наступна лексема <ident>
Блок 13 - Виконання функції розбору виразів
Блок 14 - Перевіряє чи наступна лексема Do
Блок 15 - Виконання функція розбору циклу
Блок 16 – Якщо інша лексема?
Блок 17 – Функція помилки
Блок 18 - Перевіряє чи наступна лексема End
Блок 19 – Якщо не фініш виконання функції помилки;
Блок 20 - Завершення Синтаксичного аналізатора
3.4.2 Опис програми реалізації синтаксичного та семантичного аналізатора
Принцип роботи такий, що на таблиця лексем подається на вхід синтакчиного аналізатора. Після чого по черзі здійснюємо перебір лексем та здійснюємо аналіз класів лексем. У випадку коли ми прийшли до ситуації коли після лексеми слідує лексема, яка має некоректний клас – помилка. Здійснюється також перевірка чи не йдуть підряд 2 лексеми одного класу, якщо таке присутнє – помилка. При виявленні у виразі даних різних типів – помилка.
При кожному знаходженні помилки в таблицю помилок додається одне поле.
3.5.Розробка генератора коду
3.5.1 Розробка граф-схеми алгоритму
Так як генерація коду тісно пов’язана з синтаксичним аналізом, тобто код конструкції генерується відразу після розпізнавання.
Рис 3.Граф-схема роботи генератора коду
Блок 1 – Початок генератора коду
Блок 2 – Основна функція генератора
Блок 3 – Перевіряє чи наступна лексема Data
Блок 4 – Виконання функції генератора блоку оголошення змінних
Блок 5 - Перевіряє чи наступна лексема Longint
Блок 6 - Виконання функції генератора цілочисельних змінних
Блок 7 - Перевіряє чи наступна лексема Body
Блок 8 - Перевіряє чи наступна лексема Write
Блок 9 - Виконання функції генератора функції виводу
Блок 10 - Перевіряє чи наступна лексема Read
Блок 11 - Виконання функція генератора функції вводу
Блок 12 - Перевіряє чи наступна лексема <ident>
Блок 13 - Виконання функції генератора виразів
Блок 14 - Перевіряє чи наступна лексема Do
Блок 15 - Виконання функція генератора циклу
Блок 16 - Перевіряє чи наступна лексема End
Блок 17 – - Виконання функція генератора завершення програми
Блок 18 - Завершення Генератора коду
3.5.2 Опис програми реалізації генератора коду
Відбувається перебір лексем із таблиці. Знайденій лексемі буде вставлено відповідний еквівалентний асемблер ний код в проміжному представленні даних.
Наприклад, якщо ми зустрінемо ключове слово Data, вставиться опис сегменту даних,а при зустрічі ключового слова Read - згенерується виклик функції Read.
Для виключення можливості помилок імена ідентифікаторів будуть генеруватися в асемблерний код зі змінами.
В розробленому трансляторі генератор коду послідовно викликає функції,які записують у вихідний файл частини коду.
4. Опис інтерфейсу та інструкції користувачеві
Запуск програми можна здійснити запустивши виконавчий файл.Дана програма працює на платформі .NET, тому для її запуску повинна бути встановлена платформа net framework(2 – 4 версія). Notepad містить редактор тексту і транслятор. Редактор тексту дозволяє відкривати файли з розширенням *.v30. /
Рис 4.Вікно редактора тексту
Даний редактор виконує функції введення і редагування тексту,збереження файлу, відкриття готового файлу і т.д.
Коли код був введений можна його транслювати. Це можна виконати нажавши клавішу F5.
Щоб отримати виконавчий файл потрібно згенерований код скомпілювати за допомогою ml.exe який вбудований в MS Visual Studio 2010.
5. Відлагодження та тестування програми
Відлагодження програми відбувається на основі спеціально створених тестів за допомогою автоматизованого відлагоджувача який присутній в середовищі Ms Visual Studio 2010.За допомогою точок переривань відбувається зупинка виконання програми в тих місцях де відбулася логічна помилка або в місцях визначених студентом.
5.1. Виявлення лексичних помилок.
Помилкою на етапі лекчиного аналізу може бути лише нерозпізнана лексема. Якщо така була то вона буде занесена в таблицю лексем. Після чого на етапі синтаксичного аналізу буде згенерована помилка.
/
Рис 5.Виявлення лексичних помилок
5.2. Виявлення синтаксичних і синтаксичних помилок.
Основна кількість помилок виявляється на етапі синтаксичного аналізу. Ці помилки пов*язані з невідповідністю вхідних конструкцій граматиці мови. Всі помилки заносяться в таблицю помилок. Семантичні аналізатор перевіряє чи оголошенні змінні.
/
Рис 6.Виявлення синтаксичних і семантичних помилок
5.4. Загальна перевірка коректності роботи транслятора.
Загальна перевірка полягає в транслюванні завідомо коректної вхідної програми з використанням всіх можливостей мови в асемблерний код та перевірці на правильність виконання програми попередньо скомпільованої та злінкованої за допомогою ml.exe
Текст програми:
Name Prog;
Data
Longint _a ->4;
Longint _b;
Body
_b -> _a ++ 4;
Write(_b);
End
Результати роботи програми:
/
Висновки
Підчас виконання курсової роботи:
1. Складено формальний опис мови програмування у формі розширеної нотації Бекуса-Наура, дано опис усіх символів та ключових слів.
2. Створено транслятор мови програмування Prog, а саме:
2.1.1. Розроблено лексичний аналізатор, здатний розпізнавати лексеми, що є описані в формальному описі мови програмування, та додані під час безпосереднього використання транслятора.
2.1.2. Розроблено синтаксичний аналізатор на основі рекурсивного спуску. Дерево виклику функцій згідно правил записаних в нотації у формі Бекуса-Наура.
2.1.3. Розроблено генератор коду, який починає свою роботу під час коректного виявлення кожної конструкції і генерує код який відповідає кожній конструкції вхідної мови. Проміжним кодом генератора є програма на мові Assembler(i586). Вихідним кодом є машинний код, що міститься у виконуваному файлі
3. Проведене тестування транслятора за допомогою тестових програм за наступними пунктами: Виявлення лексичних помилок. Виявлення синтаксичних помилок.
Список використаної літератури
Ахо и др. Компиляторы: принципы, технологии и инструменты.: Пер с англ. – М.: Издательський дом «Вильямс». 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 nmb_leks;
public string Value
{
get { return str_value; }
set { str_value = value; }
}
public int Number
{
get { return num_leks; }
set { nmb_leks = value; }
}
}
public class Iden_info
{
private string id_leks="";
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; }
}
}
public class Lexem
{
private int id=0;
private string leks = "";
private string komments = "";
private int type = 0;
private int typeKey = -1;
public int Id
{
get { return id; }
set { id = value; }
}
public int Type
{
get { return type; }
set { type = value; }
}
public int TypeKeyword
{
get { return typeKey; }
set { typeKey = value; }
}
public string Lex
{
get { return leks; }
set { leks = value; }
}
public string Comments
{
get { return komments; }
set { komments = value; }
}
}
static class Rozbir
{
public static bool isStart = false;
public static int num = 0;
public static Hashtable ident = new Hashtable();
public static ArrayList leksems = new ArrayList();
public static Hashtable const_str = new Hashtable();
public static Hashtable const_int = new Hashtable();
public static ArrayList lines = new ArrayList();
public static Iden_info id_info;
static Const_Strings strs;
static Lexem lexem;
public static void Parse(string line)
{
line = line + '\n';
for (int i = 0; i < line.Length;i++)
{
if (line.Length == 1)
continue;
if (line[i] >= 'A' && line[i] <= 'Z')
{
i = RozbirKeyWords(line, i) - 1;
continue;
}
if (line[i] >= 'a' && line[i] <= 'z')
{
i = RozbirIdent(line, i)-1;
continue;
}
if ((line[i] >= '0' && line[i] <= '9') || line[i] == '-')
{
if (line[i] == '-')
i = RozbirNum(line, i,true)-1;
else
i = RozbirNum(line, i, false) - 1;
continue;
}
switch (line[i])
{
case '=':
leksem = new Lexem();
leksem.Id = num;
num++;
leksem.Lex = "=";
leksem.Type = 5;
leksem.Comments = "Оператор порівняння";
leksem.Add(leksem);
break;
case '!':
leksem = new Lexem();
leksem.Id = num;
num++;
leksem.Lex = "!";
leksem.Type = 6;
leksem.Comments = "Логічний оператор";
leksem s.Add(leksem);
break;
case '&':
leksem = new Lexem();
leksem.Id = num;
num++;
leksem.Lex = "&";
leksem.Type = 7;
leksem.Comments = "Логічний оператор";
leksems.Add(leksem);
break;
case '|':
leksem = new Lexem();
leksem.Id = num;
num++;
leksem.Lex = "|";
leksem.Type = 8;
leksem.Comments = "Логічний оператор";
leksems.Add(leksem);
break;
case '<':
if (line[i + 1] == '>')
{
leksem = new Lexem();
leksem.Id = num;
num++;
leksem.Lex = "<>";
leksem.Type = 9;
leksem.Comments = "Оператор порівняння";
leksems.Add(leksem);
i++;
}
else
{
leksem = new Lexem();
leksem.Id = num;
num++;
leksem.Lex = "<";
leksem.Type = -1;
leksem.Comments = "Нерозпізнана лексема";
leksems.Add(leksem);
}
break;
case '>':
if (line[i + 1] == '>')
{
leksem = new Lexem();
leksem.Id = num;
num++;
leksem.Lex = "->";
leksem.Type = 10;
leksem.Comments = "Оператор присвоєння";
leksems.Add(leksem);
i++;
}
else
{
leksem = new Lexem();