Міністерство освіти і науки України.
Національний університет “Львівська політехніка”
ІКТА
Кафедра ЕОМ
КУРСОВА РОБОТА
з предмету:
“Системне програмування”
на тему:
“Розробка системних програмних модулів та компонент систем програмування”
Індивідуальне завдання “Розробка транслятора з вхідної мови програмування”
Варіант №12
В даній курсовій роботі здійснена розробка транслятора з вхідної мови програмування, заданої завданням, в файл Асемблера з подальшою його компіляцією і створенням виконавчого файлу. Дана програма перевіряє на помилки (синтаксичні, семантичні, лексичні) вхідний файл і при їх присутності видає у файл повідомлення про помилки. У курсовій роботі реалізовано прямий лексичний аналізатор,метод синтаксичного розбору – нисхідний.
Курсова робота виконана у середовищі Microsoft Visual Studio 2010, лістинг програми наведений у додатку А. Також у курсовій роботі наведено граф-схеми роботи лексичного і синтаксичного аналізаторів, генератора коду, детальний опис мови, описано процес розробки програми транслятора на рівні тексту програми.
До проекту додано результати тестування програми та текст програми транслятора.
Зміст
Завдання на курсову роботу 5
Вступ 6
Огляд методів та способів проектування трансляторів 7
Формальний опис вхідної мови програмування
Деталізований опис вхідної мови в термінах розширеної нотації 8 Бекуса -Наура
Опис термінальних символів та ключових слів 10
Розробка транслятора вхідної мови програмування
Вибір технології програмування 12
Проектування таблиць транслятора та вибір структур даних 13
Розробка лексичного аналізатора
Розробка граф-схеми алгоритму 15
Опис програми реалізації лексичного аналізатора 16
Розробка синтаксичного та семантичного аналізатора
Розробка дерев граматичного розбору 17
Розробка граф-схеми алгоритму 18
Опис програми реалізації синтаксичного та семантичного 18 аналізатора
Розробка генератора коду
Розробка граф-схеми алгоритму 20
Опис програми реалізації генератора коду 20
Опис інтерфейсу та інструкції користувача 22
Відлагодження та тестування програми
Виявлення лексичних помилок 23
Виявлення синтаксичних помилок 24
Виявлення семантичних помилок 25
Загальна перевірка коректності роботи транслятора 26
Висновки 28
Список літератури 29
Додатки
А. Лістинг програми 30
ЗАВДАННЯ
Варіант 12;
Розробити транслятор вхідної мови програмування , короткий опис якої подано нижче:
- типи даних: integer_2;
- оператор вводу: Read ();
- оператор виводу: Write ();
- блок тіла програми:
- оператор: For-To-Next (Бейсік);
- регістр ключових слів: Up-Low перший символ Up;
- регістр ідентифікаторів: Low6 ;
- операції арифметичні: ++, --, **, Div,Mod;
- операції порівняння: ==, !=, >>, <<
- операції логічні: !, &, |;
- коментар: //….//
- ідентифікатори змінних, числові константи;
- оператор присвоєння: :: ;
Для отримання виконавчого файлу на виході розробленого транслятора скористатися програмами tasm.exe (компілятор мови асемблера) і tlink.exe (редактор зв’язків).
ВСТУП
Компілятор (англ. Compiler від англ. to compile збирати в ціле) - комп'ютерна програма , що перетворює (компілює) програмний код, написаний певною мовою програмування, на семантично еквівалентний код в іншій мові програмування, який, як правило, необхідний для виконання програми машиною.
Перші компілятори з'явилися на початку 50-х років.
Транслятор – це той самий компілятор, з тею різницею, що генерує він не об’єктний код, а код на іншій мові програмування.
Проблема трансляції полягає в пошуку відповідності тексту вхідної програми конструкціям, що визначені граматикою. Граматика визначає форму або синтаксис допустимих виразів мови. Тому текст вхідної мови зручно подавати у вигляді послідовності лексем, що є неподільними одиницями мови. За допомогою транслятора програміст повинен мати можливість редагувати текст вхідної мови. Для цього він має виявляти всі невідповідності тексту програми конструкціям мови і у випадку відсутності помилок генерувати код.
Основні задачі, які виконуються різними компіляторами та трансляторами, по суті, одні і ті ж. Розуміючи ці задачі, існує можливість створювати транслятори для різних початкових мов і цільових машин з використанням одних і тих же базових технологій.
1. ОГЛЯД МЕТОДІВ ТА СПОСОБІВ ПРОЕКТУВАННЯ ТРАНСЛЯТОРІВ
Створення компіляторів відбувається в певних конкретних умовах: для різних мов, для різних цільових платформ, з різними вимогами для створення компіляторів. Є такі методи створення компіляторів:
1. Прямий метод- цільовою мовою і мовою реалізації є асемблер.
2. Метод розкрути- саме цей метод і використовується у даній курсовій роботі, тобто вибирається інструмент (в даній курсовій це мова асемблер), для якого вже існує компілятор.
3.Використання крос-трансляторів.
4.З використанням віртуальних машин – дає спосіб отримати переносимо програму.
5. Компіляція на ходу.
В даній курсовій роботі згідно із завданням для парних варіантів необхідно реалізувати нисхідний метод граматичного розбору. Нисхідний метод полягає в побудові дерева розбору, починаючи від кореневої вершини. Розбір полягає в заповненні проміжку між початковим нетерміналом і символами вхідного ланцюжка правилами, виведеними з початкового нетермінала. Підстановка ґрунтується на тім факторі, що коренева вершина є вузлом, що складається з листів, що є ланцюжком терміналів і нетерміналів одного з альтернативних правил, породжуваних початковим нетерміналом. Правило, що підставляється, у загальному випадку обирається довільно. Замість нових нетермінальних вершин здійснюється підстановка виведених з них правил. Процес протікає доти, поки не будуть установлені всі зв'язки дерева, що з'єднують кореневу вершину і символи вхідного ланцюжка, чи поки не будуть перебрані всі можливі комбінації правил. В останньому випадку вхідний ланцюжок відкидається. Побудова дерева розбору підтверджує приналежність вхідного ланцюжка даній мові.
В даній курсовій роботі реалізовано метод рекурсивного спуску оскільки він є одним із найпростіших нисхідних методів. Для кожного правила граматики створюється окрема процедура. Перший символ із вхідної послідовності має одночасно ідентифікувати правило відповідно до процедури.
2. ФОРМАЛЬНИЙ ОПИС ВХІДНОЇ МОВИ ПРОГРАМУВАННЯ
2.1. Деталізований опис вхідної мови в термінах розширеної нотації Бекуса-Наура.
Однією з перших задач, що виникають при побудові компілятора, є визначення вхідної мови програмування. Для цього використовують різні способи формального опису, серед яких я застосувала розширену нотацію Бекуса-Наура (Backus/Naur Form - BNF).
<program>::=Program<name>Variable<VAR_blok>Start<code_blok>Stop
<VAR_blok> ::=<declarations> [{;<declarations>} ];.
<name>::=<letter>[<l _or_n>].
<declarations>::=<type_i><declaration_i> [{,<declaration_i>}] .
<type_i> ::=Integer_2.
<declaration_i> ::= <ident> [ << <const_i>] .
<ident> ::= <letter>[<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 .
<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_b> .
<expression_i>::= <term> [{++ <term> | -- <term>}]|<term1>
<term1>::=[{<ident>|<const_i>}] <expression_b> [{<ident>|<const_i>}]? <number>:<number>
<term>::=<ident>|<const_i>|<factor>|<factor_b>;
<factor>::=[{** <term> | Div <term>| Mod <term> | <brackets>}].
<brackets>::=(<expression_i>).
<expression_b>::= <term_b> [{== <term_b> | != <term_b> | >> <term_b>| << <term_b>}].
<term_b>::=<ident>|<const_b>|<factor_b>.
<factor_b>::=<term_b> [{ |<term_b> | <and> |<not>|<or>}].
<and>::= [{ &<term_b>}]
<or>::= [{ |<term_b>}]
<not>::=!<term>
<cycle> ::= For (<ident>)To(<ident>)Next Label <code_blok> Label.
<Read> ::= Read(<ident>); .
<Write> ::= Write(<expression_i>|<string>); .
<string> ::= “<l_or_n>[{<l_or_n>}]” .
2.2. Термінальні символи та ключові слова.
Визначимо окремі термінальні символи та нерозривні набори термінальних символів (ключові слова):
;
,::++
--
**
Div
Mod
&
!
?
:
|
>>
<<
==
!=
()
Integer_2
Program
Variable
Start
Stop
Read
Write
For
To
Next
До термінальних символів віднесемо також усі цифри (0-9), латинські букви (a-z, A-Z), символи табуляції,символ переходу на нову стрічку, пробілу, знаку “-“ .
3. РОЗРОБКА ТРАНСЛЯТОРА ВХІДНОЇ МОВИ ПРОГРАМУВАННЯ
3.1. Вибір технології програмування.
Для ефективної роботи створюваної програми важливу роль відіграє попереднє складення алгоритму роботи програми, алгоритму написання програми і вибір технології програмування.
Тому при складанні транслятора треба брати до уваги швидкість компіляції, якість об’єктної програми. Проект повинен давати можливість просто вносити зміни.
В реалізації мов високого рівня часто використовується специфічний тільки для компіляції засіб “розкрутки”. З кожним транслятором завжди зв`язані три мови програмування: Х – початкова, Y – об`єктна та Z – інструментальна. Транслятор перекладає програми мовою Х в програми, складені мовою Y, при цьому сам транслятор є програмою написаною мовою Z.
При розробці даного курсового проекту був використаний нисхідний метод синтаксичного аналізу, використовувався метод рекурсивного спуску.
Також був обраний прямий метод лексичного аналізу. Характерною ознакою цього методу є те, що його реалізація відбувається без повернення назад. Його можна сприймати, як один спільний скінченний автомат. Такий автомат на кожному кроці читає один вхідний символ і переходить у наступний стан, що наближає його до розпізнавання поточної лексеми чи формування інформації про помилки. Для лексем, що мають однакові підланцюжки, автомат має спільні фрагменти, що реалізують єдину множину станів. Частини, що відрізняються, реалізуються своїми фрагментами.
3.2. Проектування таблиць транслятора.
Використання таблиць значно полегшує створення трансляторів, тому у даному випадку використовуються кілька таблиць:
1) Таблиця лексем з елементами, які мають таку структуру:
Lexema LexAr[1000]; //таблиця лексем з елементами типу Lexema
typedef struct Lexem
{
char name[50]; //ім’я
int value; //значення
TypeOfLex type; //тип
}Lexema;
2)Таблиця лексичних класів
enum TypeOfLex
{
program_,
nameprogram,
data,
integer_2,
start,
end,
label_,
for_,
to_,
minus,
next_,
ifcond, //?
result, //:
put,
get,
var,
number,
separator, //;
koma, //,
open, //(
close, //)
add_,
sub_,
not_,
and_,
or_,
mul_,
div_,
mod_,
is,
eq,
neq,
ng,
nl,
eof,
us //неопізнаний символ
};
2) Таблиця ідентифікаторів з елементами типу Identificator та додатковою цілочисельною змінною цілочисельного типу в якій зберігається кількість визначених змінних. Структура елементів така:
typedef struct ID
{
char name[50]; //ім’я
int val; //значення
}Identificator;
3.3. Розробка лексичного аналізатора.
3.3.1Розробка граф-схеми алгоритму
Рис 1.Граф-схема роботи лексичного аналізатора (частина 1)
Рис 2.Граф-схема роботи лексичного аналізатора (частина 2)
3.3.2Опис програми реалізації лексичного аналізатора
Граф схема роботи лексичного аналізатора наведена на рис1, рис2. Принцип роботи такий: в даній курсовій роботі реалізовано прямий лексичний аналізатор, тобто він реалізується без повернення назад.Спочатку виділяється лексема і перевіряється до якого класу лексем вона належить. Якщо лексема належить до класу ключові слова то це має бути якесь із цих слів: Program, Variable, Start, Stop, Read,Write, For,To, Next, Div,Mod, Integer_2. Якщо лексема належить до класу знак операції то це операції: ++, -- ,**, &, | , ! ,<<,>>,!=,==, ?, : . Якщо лексема належить до класу ідентифікатори, то вона має бути написана маленькими буквами і їх кількість не більше 6, в іншому випадку лексема розпізнається як невідоме ім’я. Якщо лексема дорівнює пробілу, табуляції або коментар (в моєму варіанті це // … //), то ця лексема ніяк не розпізнається вона просто пропускається.
В даній курсовій роботі у програмі при розробці лексичного аналізатора були створені такі функції
int LexicalAnalis(FILE*); -- виконує лексичний аналіз і створює масив лексем.
void PrintLexArray(); -- видруковує в файл масив лексем.
3.4.Розробка синтаксичного аналізатора
3.4.1Розробка дерева граматичного розбору
В даній курсовій роботі використовується нисхідний метод розбору. Одним із найпростіших нисхідних методів є метод рекурсивного спуску - це метод синтаксичного аналізу "зверху вниз", у якому вхід обробляється виконанням низки рекурсивних процедур. З кожним нетерміналом граматики пов'язана одна така процедура.
Ось наприклад дерево розбору(рис 3) для прикладу:
Program<1>
Variable Integer_2 a
Start
a::3;
For 1 To 3 Next Label
a:: a ++ 3;
Label
End
Рис 3. Дерево розбору
Послідовність викликів процедур неявно визначає дерево розбору. Відповідно до цього дерева і відбувається генерація коду.
3.4.2 Розробка граф-схеми алгоритму
Рис 4.Граф-схема роботи синтаксичного аналізатора
3.4.3 Опис програми реалізації синтаксичного та семантичного аналізатора
В даному трансляторі синтаксичний аналізатор працює паралельно почергово з генератором коду, частиною якого він і є. Принцип роботи такий: аналізується черговий набір послідовних лексем та викликається відповідна генерація асемблерного коду. Потім знову вище описані дії повторюються поки не обробляться всі лексеми.
Семантичний аналіз в даній програмі виконує такі функції:
Перевірка на звернення до неініціалізованих ідентифікаторів;
2)перевірка на наявність неоголошених ідентифікаторів
Блок –схема роботи синтаксичного аналізатора наведена на рис 4. Оскільки синтаксичний аналізатор працює паралельно із генератором коду то при зустрічі чергової лексеми генерується код.Код генерується тільки у тому випадку коли у програмі немає помилок. При написанні синтаксичного аналізатора використовувались такі функції
int CheckProgram(FILE*); -- перевіряє вхідну програму на коректність (згідно створеної таблиці лексем), якщо виявлені помилки, то програма видає повідомлення про помилки та завершує роботу.
int CreateIdTab(FILE*); -- створює таблицю ідентифікаторів.
void CodeGenerator(FILE*, char*); -- за таблицями ідентифікаторів та лексем генерує вихідний код на мові асемблера.
Вище описані функції використовують в свою чергу наступні:
Lexema* GetLex(FILE*); -- повертає в LexicalAnalis чергову лексему.
void CheckPresentGetPut(); -- перевіряє присутність відповідних операторів в програмі.
3.5.Розробка генератора коду
3.5.1 Розробка граф-схеми алгоритму
Оскільки синтаксичний аналізатор працює паралельно з генератором коду то граф-схема генератора коду така ж як і синтаксичного аналізатора (рис 4)
3.5.2 Опис програми реалізації генератора коду
Генерація коду – це машинно-залежний етап компіляції, під час якого відбувається побудова машинного еквівалента вхідної програми. Зазвичай входом для генератора коду служить проміжна форма представлення програми, а на виході може з’являтися об’єктний код або модуль завантаження.
В даному трансляторі генератор коду послідовно викликає окремі функції, які записують у вихідний файл частини коду.
void PrintTitle(FILE*, char*);
видруковує в вихідний файл заголовок програми.
void PrintData(FILE*);
видруковує в вихідний файл сегмент коду, який крім введених змінних за умови присутності операторів вводу/виводу (CheckPresentGetPut();) і службову інформацію для них та додаткові буфери.
void PrintInit(FILE*);
видруковує в вихідний файл код ініціалізації сопроцесора.
void PrintEnding(FILE*);
видруковує заключну частину коду та викликає наступні функції для запису:
void PrintGet(FILE*);
видруковує в вихідний файл процедуру вводу.
void PrintPut(FILE*);
видруковує в вихідний файл процедуру виводу.
void PrintMOD(FILE*);
видруковує в вихідний файл процедуру mod_.
void PrintAND(FILE*);
видруковує в вихідний файл процедуру and_.
void PrintOR(FILE*);
видруковує в вихідний файл процедуру or_.
void PrintNOT(FILE*);
видруковує в вихідний файл процедуру not_.
void PrintEQ(FILE*);
видруковує в вихідний файл процедуру eq.
void PrintNL(FILE*);
видруковує в вихідний файл процедуру nl.
void PrintNG(FILE*);
видруковує в вихідний файл процедуру ng.
void PrintCode(FILE*);
видруковує в вихідний файл основний код програми. Дана функція напряму чи опосередковано викликає наступні функції:
void GenASMCode(const char *, FILE*);
генерує код для окремих операцій.
Використовує наступну функцію:
int ConvertToPostfixForm(int); -- перетворює вирази в постфіксну форму. Використовує :
bool IsOperation(TypeOfLex); -- перевіряє чи лексема є операція.
bool Prioritet(TypeOfLex,StackType);
4. Опис інтерфейсу та інструкції користувачеві
Створений компілятор є програмою що запускається з командної стрічки з параметром, а саме іменем вхідного файлу в якому записана програма на вхідній мові. Файл повинен мати розширення .s12. Отже формат введення повинен бути таким:
S12c <ім’я програми>.s12
У випадку некоректного введення програма видасть повідомлення про помилку і завершить своє виконання.
Якщо коректно введене ім’я вхідного файлу та вдалось його відкрити, то компілятор почне його опрацювання. Початковою фазою обробки є лексичний аналіз (розбиття на окремі лексеми), наступною -- перевірка на правильність написання програми (вхідної). У випадку, коли виявлені помилки, компілятор видасть повідомлення про їхню присутність, вкаже в якому файлі можна ці помилки переглянути та завершить своє виконання. Якщо ж програма написана вірно, то компілятор перейде на наступну фазу генерації асемблерного коду та вкаже ім’я результуючого файлу з розширенням .asm та завершить своє виконання.
Для одержання виконавчого файлу необхідно скористатись програмами tasm.exe filename.asm для компіляції та tlink filename.obj для лінкування. Всі файли повинні міститися в одному каталозі.
5. ВІДЛАГОДЖЕННЯ ТА ТЕСТУВАННЯ ПРОГРАМИ
Відлагодження та тестування компілятора проводиться з використанням кількох вхідних програм з навмисне введеними помилками та з коректною програмою для загальної перевірки роботи транслятора.
5.1. Виявлення лексичних помилок.
Виявлення лексичних помилок відбувається на стадії лексичного аналізу. При розборі вхідної програми на окремі лексеми лексичний аналізатор перевіряє чи відповідає отримана лексема лексиці заданої мови програмування. У випадку неспівпадіння лексемі присвоюється тип “невпізнаної лексеми”. Повідомлення про такі помилки можна побачити лише після виконання процедури перевірки таблиці лексем.
Приклад виявлення:
Текст програми з лексичними помилками.
#Program <12>
Variable
Іnteger_2 a, fffffff ;
Start
Write(a);
Stop
Текст файлу з повідомленнями про лексичні помилки.
S12 compiler
Designed by Yulia Sholohon
Error file:
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
program_
nameprogram
data
Іnteger_2
a,
fffffff; (Unknown symbol)
;
start
Write(a);
Stop
5.2. Виявлення синтаксичних помилок.
Виявлення синтаксичних помилок відбувається на стадії перевірки програми на коректність окремо від синтаксичного аналізу. При цьому перевіряється окремо кожне твердження яке може бути або виразом, або оператором (циклу, вводу/виводу), або оголошенням, та перевіряється структура програми в цілому.
Текст програми з синтаксичними помилками.
#Program <12>
Variable
Іnteger_2 a, b ;
Start
b::a++(b;
Write(a);
Stop
Текст файлу з повідомленнями про синтаксичні помилки.
S12 compiler
Designed by Yulia Sholohon
Error file:
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
program_
nameprogram
data
Іnteger_2 a, b;
start
b::a++(b; (illegal expression)
Write(a);
Stop
5.3. Виявлення семантичних помилок.
Суттю виявлення семантичних помилок є перевірка числових констант на відповідність типу integer_2 тобто знаковому цілому числу з відповідним діапазоном значень.
Текст програми з семантичними помилками.
#Program <12>
Data
a, b ;
Start
b::a++b;
Write(a);
Stop
Текст файлу з повідомленнями про семантичні помилки.
S12 compiler
Designed by Yulia Sholohon
Error file:
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
program_
nameprogram
data
(Expected " Іnteger_2 ")
a,
b;
start
b::a++b;
Write(a);
Stop
5.4. Загальна перевірка коректності роботи транслятора.
Загальна перевірка полягає в транслюванні завідомо коректної вхідної програми з використанням всіх можливостей мови в асемблерний код та перевірці на правильність виконання програми попередньо скомпільованої та злінкованої за допомогою tasm та tlink.
Текст коректної програми.
#Program <12>
Variable
Іnteger_2 a, o,i,pp,oo,y,lab ;
Start
// komentar//
a::4;
i::3;
o::5;
For 1 To 5 Next Label
a:: a -- 6;
Write(a);
Label
pp::7;
oo::12;
Write(pp ++ oo ** 5 );
Write(0 & 0 | 1);
Write(1 | 0 & 0);
Read(y);
Read(lab);
Write( 10 Mod lab);
Write( y++(8 Div 2)--2);
Write(i>>o? 5:10);
Write(i<<o? 15:1);
Write(i==o? 1:2);
Write(i!=o? 1:2);
Stop
. Результати виконання програми.
ВИСНОВКИ
В процесі виконання курсової роботи було виконано наступне:
Складено формальний опис мови програмування S12c, в термінах розширеної нотації Бекуса-Наура, виділено усі термінальні символи та ключові слова.
Створено компілятор мови програмування S12c, а саме:
Розроблено прямий лексичний аналізатор, орієнтований на розпізнавання лексем, що є заявлені в формальному описі мови програмування.
Розроблено синтаксичний аналізатор на основі методу рекурсивного спуску. Складено деталізований опис вхідної мови в термінах розширеної нотації Бекуса-Наура
Розроблено генератор коду, відповідні процедури якого викликаються після перевірки синтаксичним аналізатором коректності запису чергового оператора, мови програмування S12c. Вихідним кодом генератора є програма на мові Assembler(i8086).
Проведене тестування компілятора на тестових програмах за наступними пунктами:
На виявлення лексичних помилок.
На виявлення синтаксичних помилок.
Загальна перевірка роботи компілятора.
Тестування не виявило помилок в роботі компілятор, і всі помилки в тестових програмах на мові S12c були успішно виявлені і відповідно оброблені.
В результаті виконання даної курсової роботи було засвоєно методи розробки та реалізації компонент систем програмування.
СПИСОК ЛІТЕРАТУРИ
Шильдт Г. С++. – Санкт-Петербург: BXV, 2002. – 688 с.
Хантер Р. Проектирование и конструирование компиляторов. – М.: Финансы и статистика, 1984. – 232 с.
Шамис В. А. С++Builder 4 Техника визуального программирования. – М.: Нолидж, 2000. – 656 с.
Ахо А., Ульман Дж. Теория синтаксического анализа, перевода и компиляции. – М.: Мир, 1978. – т.1, 612 с.
Серебряков В.А. Лекции по конструированию компиляторов. – М.: МГУ, 1993.
В.С. Проценко, П.Й. Чаленко, А.Б.Ставровський “Техніка програмування мовою Сі”.-Київ “Либідь”, 1993. – 224с.
Б.Керниган, Д.Ритчи “Язык программирования Си”. – Москва “Финансы и статистика”, 1992. – 271с.
Н.Г.Голубь “Исскуство программирования на асемблере.Лекции и упражнения”. –Киев “DiaSoft”, 2002 – 642с.
Л.Бэк “Введение в системное програмирование”. – М.: Мир, 1999
ДОДАТКИ
Додаток А. Лістинг програми
// Pruclad.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include "stdio.h"
#include "malloc.h"
#include "string.h"
#include "stdlib.h"
#include "ctype.h"
enum TypeOfLex
{
program_,
nameprogram,
data,
integer_2,
start,
end,
label_,
for_,
to_,
minus,
next_,
ifcond,
result,
put,
get,
var,
number,
separator, //;
koma, //,
open, //(
close, //)
add_,
sub_,
not_,
and_,
or_,
mul_,
div_,
mod_,
is,
eq,
neq,
ng,
nl,
eof,
us //неопізнаний символ
};
//DATA
typedef struct Lexem
{
char name[50];
int value;
TypeOfLex type;
}Lexema;
typedef struct ID
{
char name[50];
int val;
}Identificator;
typedef struct L
{
Lexema LexAr[1000]; //Таблиця лексем
int Len; //
Identificator IdTable[50]; //Таблиця ідентифікаторів
int idnum; //
int LTable[50]; //
int TopL; //
bool IsPresentPut; //Ознака присутності оператора put();
bool IsPresentGet; //Ознака присутності оператора get();
int Errors;
char InputFileName[50];
char OutputFileName[50];
int PFExpr[200]; //Буфер для виразу в постфіксній формі
}Data;
typedef struct Stacks
{
int st[200];
int top;
}StackType;
typedef class stack
{
public:
StackType S;
void Init(StackType* s)
{
s->top = -1;
}
void Push(int i, StackType* s)
{
if(IsFull(s))
{
puts("Stack error (is full)");
exit(0);
}
else
{
++s->top;
s->st[s->top] = i;
}
}
int Pop(StackType* s)
{
int i;
if(IsEmpty(s))
{
puts("Stack error (is empty)");
exit(0);
}
else
{
i = s->st[s->top];
--s->top;
}
return i;
}
bool IsEmpty(StackType* s)
{
if(s->top == -1)
{
return true;
}
else
{
return false;
}
}
bool IsFull(StackType* s)
{
if(s->top == 199)return true;
else return false;
}
void prints(StackType s)
{
int i=0;
for(;i<10;++i)
{
printf("%d_",s.st[i]);
}
}
}Stack;
Stack s; //Стек
Data Reg; //Структура-реєстр в якій зберігаються всі дані програми
FILE *InF, *OutF, *ErrorF;
//DECLARATIONS OF FUNCTIONS
Lexema* GetLex(FILE*); //Виділяє із вхідного файлу наступну лексему
int LexicalAnalis(FILE*); //Утворює таблицю лексем
void PrintLexArray(); //видруковує лексеми в файл
int CreateIdTab(FILE*); //Створює таблицю лексем
void CheckPresentGetPut(); //Перевіряє присутність процедур вводу/виводу
void PrintGet(FILE*); //Видруковує в вихідний файл код виклику процедури вводу
void PrintPut(FILE*); //Видруковує в вихідний файл код виклику процедури виводу
void PrintTitle(FILE*, char*); //Видруковує в вихідний файл код заголовку програми
void PrintData(FILE*); //Видруковує в вихідний файл код сегменту даних
void PrintInit(FILE*); //Видруковує в вихідний файл код ініціалізації сопроцесора
void PrintEnding(FILE*); //Видруковує в вихідний файл код завершення програми та необхідні функції
void CodeGenerator(FILE*, char*); //Видруковує в вихідний файл код програми
void CallPut(FILE*); //Видруковує в вихідний файл код процедури виводу
void CallGet(FILE*); //Видруковує в вихідний файл код процедури вводу
int ConvertToPostfixForm(int); //Перетворює вирази в постфіксну форму
bool IsOperation(TypeOfLex); //Перевіряє, чи лексема є операцією
bool Prioritet(TypeOfLex,StackType); //Перевіряє пріоритет поерацій
void PrintCode(FILE*); //Видруковує в вихідний файл код сегменту коду
void GenASMCode(const char *, FILE*); //Генерує асемблерний код для постфіксних виразів
void PrintMOD(FILE*); //Видруковує в вихідний файл код процедури mod_
void PrintAND(FILE*); //Видруковує в вихідний файл код процедури and_
void PrintOR(FILE*); //Видруковує в вихідний файл код процедури or_
void PrintNOT(FILE*); //Видруковує в вихідний файл код процедури not_
void PrintEQ(FILE*); //Видруковує в вихідний файл код процедури eq_
void PrintNL(FILE*); //Видруковує в вихідний файл код процедури nl_
void PrintNG