Розробка системних програмних модулів та компонент систем програмування

Автор: Пользователь скрыл имя, 25 Февраля 2012 в 16:19, курсовая работа

Описание работы

Незважаючи на більш ніж піввікову історію обчислювальної техніки, формально роком народження теорії трансляторів можна вважати 1957, коли з'явився перший транслятор мови Фортран, створений Бекасом. До цього часу створення трансляторів було досить "творчим" процесом. Лише поява теорії формальних мов і строгих математичних моделей дозволило перейти від "творчості" до "науки".

Содержание

Вступ
1. Огляд методів та способів проектування трансляторів
1.1. Основні поняття і визначення
1.2. Узагальнена структура транслятора
1.4. Варіанти взаємодії блоків транслятора
2. Формальний опис вхідної мови програмування
2.1. Деталізований опис вхідної мови в термінах розширеної нотації Бекуса-Наура
2.2. Опис термінальних символів та ключових слів
3. Розробка транслятора вхідної мови програмування
3.1. Вибір технології програмування
3.2. Проектування таблиць транслятора та вибір структур даних
3.3. Розробка лексичного аналізатора
3.3.1. Розробка граф-схеми алгоритму
3.3.2. Опис програмної реалізації лексичного аналізатора
3.4. Розробка синтаксичного та семантичного аналізатора
3.4.1. Розробка граф-схеми алгоритму
3.4.2. Опис програмної реалізації синтаксичного та семантичного аналізатора
3.5. Розробка генератора коду
3.5.1. Розробка граф-схеми алгоритму
3.5.2. Опис програмної реалізації генератора коду
4. Опис інтерфейсу та інструкції користувача
4.1. Опис інтерфейсу
4.1.1. Головне меню та панель інструментів.
4.2. Інструкція програміста
4.2.1. Алфавіт мови
4.2.2. Коментарі
4.2.3. Тип даних
4.2.4. Розділи, використовувані при написанні програм
4.2.5. Типи операцій
4.2.6. Типи операторів
5. Відлагодження та тестування програми
5.1. Виявлення лексичних помилок
5.2. Виявлення синтаксичних помилок
5.3. Виявлення семантичних помилок
5.4. Загальна перевірка коректності роботи транслятора
Висновки
Список літератури
Додатки
А. Лістинг програми

Работа содержит 1 файл

asm_cource_k9.doc

— 1,020.00 Кб (Скачать)

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

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

 

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


Можна виділити два основних варіанти взаємодії блоків транслятора:

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

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

На основі двох основних варіантів можна також створювати їхні різноманітні сполучення.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

2. Формальний опис вхідної мови програмування

2.1. Деталізований опис вхідної мови в термінах розширеної нотації Бекуса-Наура

<program>     ::= Program<ident>; Var < Data_Blok> Start <Code_Blok> Finish.

< Data_Blok >::= [Integer[<declaration> [{, <declaration>} ] ] ] ; .

<declaration> ::= <ident> [ := <const> ] .

<ident>          ::= _<h_letter>[<l _or_n>][<l _or_n>][<l _or_n>][<l _or_n>] .

<l_or_n>        ::= <l_letter>|<number> .

<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 .

<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 .

<number>       ::= 0|1|2|3|4|5|6|7|8|9 .

<const>          ::= [-]<number>[{number}] .

<Code_Blok> ::= <statement> [{<statement>}] .

<statement>    ::= <equation>|<cycle>|< Input >|< Output >|<Condition> .

<equation>     ::= <ident> := <expression2>; .

<expression1> ::= [(]<operand>[)] [{ [)]<operation1>[(]<operand>[)] }] | <expression2> .

<operand>      ::= <ident>|<const>| <expression1> .

<operation1> ::= ==|!=|Le|Ge|!!|&&| || .

<expression2> ::= [(]<operand>[)] [{ [)]<operation2>[(]<operand>[)] }] | <expression3> .

<operation2> ::= +|- .

<expression3> ::= [(]<operand>[)] [{ [)]<operation3>[(]<operand>[)] }] .

<operation3> ::= Mul|Div|Mod .

<cycle>           ::= For(<ident>:=<expression2>)DownTo (<expression2) Do

                            Start <Code_Blok> Finish.

<Condition>   ::=If(<expression1>) Start <Code_Block> Finish

< Input >        ::= Input (<ident>); .

< Output >    ::= Output (<string>|<ident>); .

<string>         ::= “{/<l_or_n >|<h_letter>/}” .

 


2.2. Опис термінальних символів та ключових слів

 

Визначимо окремі термінальні символи та нерозривні набори термінальних символів (ключові слова):

 



50

 

Program

Var

Start

Finish

Integer

Input

Output

For

DownTo

Do

If

+

-

Mul

Div

Mod

==

!=

Le

Ge

!!

&&

||

:=

/*

*/

;

,

(

)



50

 

 

До термінальних символів віднесемо усі цифри (0 - 9), латинські літери (a - z, A - Z), символи пробілу, табуляції та кінця рядка.

 

 

 


3. Розробка транслятора вхідної мови програмування

 

3.1. Вибір технології програмування

 

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

 

До переваг такого підходу можна віднести:

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

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

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

 

До недоліків варто віднести:

1.      Наявність більших обсягів проміжної інформації, з якого в цей момент часу потрібно тільки невелика частина.

2.     Зменшення швидкості трансляції через послідовне виконання фаз.

 


3.2. Проектування таблиць транслятора та вибір структур даних

 

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

 

1.      Таблиця лексем:

 

public class Lexema

    {

        public Lexema() //конструктор по замовчуванню

        {

            Type = LexemType.Null;

            Line = 0;

            Text = "";

            Value = 0;

        }

        public LexemType Type; //тип лексеми

        public int Line; //лінія, в якій зустрічається лексема

        public string Text; //текст лексеми

        public int Value; //значення лексеми, якщо це константа чи ідентифікатор,

                                    //інакше 0

    }

     List<Lexema> LexTable; //список лексем

2.      Таблиця ідентифікаторів:

 

   public class Identificator

    {

        public Identificator()  //конструктор по замовчуванню

        {

            Line = 0;

            Name = "";

            Value = 0;

            IsDeclared = false;

        }

        public int Line; //лінія, в якій зустрічається ідентифікатор

        public string Name; // ім’я ідентифікатора

        public int Value; // значення ідентифікатора

        public bool IsDeclared; // інформація чи ідентифікатор є задекларованим

    }

    public List<Identificator> IdentTable; // список ідентифікаторів

 

3.      Таблиця стрічкових констант:

 

   public struct Strings

    {

        public string Name; //ім’я стрічкової константи (присвоюється динамічно)

        public string Value; // значення стрічкової константи

    }

    public List<Strings> StringTable; // список стрічкових констант


3.3. Розробка лексичного аналізатора

 

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

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

 

 

 

 


3.3.1.     Розробка граф-схеми алгоритму

Для кращого розуміння дій, що виконуються транслятором наведемо граф -схему алгоритму лексичного аналізу (мал. 3.3.1.1):

Малюнок 3.3.1.1 (Граф -схема алгоритму лексичного аналізу)

 

Призначення блоків:

Блоки 1-2: Ініціалізують члени класу параметрами переданими в конструктор

Блок 3: Викликає функцію отримання чергової лексеми GetLexem(). Алгоритм її роботи наведено на мал. 3.3.1.2

Блок 4: Додає отриману лексему в список лексем(таблиця лексем представлена списком List<Lexema> LexTable;)

Блок 5: Перевіряє чи остання отримана лексема є кінцем файлу. Якщо так тоді переходить до блоку 7, якщо ні тоді до блоку 3.

Блок 6 - 7:  Повертають список лексем та завершують роботу лексичного аналізатора.
Граф - схема отримання лексеми:

 

 

Малюнок 3.3.1.2  ( Граф - схема отримання лексеми)

 

 

 

 


Кінцем тексту вважається символ : $

Роздільником вважається:   \n \t  <space>

Початком рядкової константи вважається: \”

Початком лексеми вважається: A – Z   0 – 9 ; : + - ! & | = ( ) ,

Зарезервовані слова:



50

 

Program

Var

Start

Finish

Integer

Input

Output

For

DownTo

Do

If

+

-

Mul

Div

Mod

==

!=

Le

Ge

!!

&&

||

:=

;

,

(

)



50

 


3.3.2  Опис програмної реалізації лексичного аналізатора:

Лексичний аналізатор програмно реалізований за допомогою класу:

 

Основним методом є функція CreateLexTable(), яка повертає таблицю лексем.

Функція працює за алгоритмом наведеному на малюнку 3.3.1.1

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

   та додається до таблиці.

 

       Зчитування наступної лексеми відбувається за допомогою функції GetLex(), яка повертає зчитану лексему, в разі помилки додає повідомлення про помилку в об’єкт LexErrors та збільшує SourcePointer на кількість прочитаних та пропущених символів.

Функція працює за алгоритмом наведеним на малюнку 3.3.1.2

-  Створюється нова лексема, з значеннями по замовчуванню, якщо в ході виконання вони не зміняться, це означає що була помилка, і лексема буде типу Null

-  Перевіряється символ в даній позиції (блоки 2, 3, 5, 14)

 

 

 

-  Якщо символ є:

      Роздільником – він пропускається (блок 4)

      Початком коментаря – пропускається коментар, в випадку якщо не знайдено закінчення коментаря – додається повідомлення про помилку.

(блоки  6, 7, 8)

Початком рядкової константи – обробляється як рядкова константа, якщо не знаходиться закінчення рядкової константи – додається повідомлення про помилку. (блоки  10, 11, 12, 13)

      Початком лексеми – символи до найближчого роздільника 

Информация о работе Розробка системних програмних модулів та компонент систем програмування