Міністерство освіти і науки України
Національний університет
„Львівська політехніка”
Кафедра ЕОМ
КУРСОВИЙ ПРОЕКТ
з курсу
“Системне програмне забезпечення”
на тему:
„Розробка простого компілятора ”
Львів-2004
Анотація
В даному курсовому проекті розроблено компілятор з СІ-подібної вхідної мови програмування . Компілятор розроблений в середовищі програмування Microsoft Visual C++ на мові ANSI С, та поданий у пояснювальній записці, а також в електронному варіанті. В пояснювальній записці подано огляд існуючих методів розробки компіляторів, детальний опис мови, а також описано процес розробки програми компілятора на рівні блок-схем і тексту програми. До проекту додано результати тестування програми.
Компілятором називається програма перекладу (компіляції) початкової програми, записаною вхідною мовою, в еквівалентну їй об`єктну програму.
Компілятори дозволяють створювати об`єктні модулі, які пізніше, після етапу зв`язування відлагоджувачем, перетворюються у виконавчі файли.
Потреба різноманіття компіляторів дуже важлива, це прямо залежить від вхідної мови, яку він перекладає. Кожна з них реалізує клас задач, необхідних для користувача.
Кажучи більш простіше, існує виконавець – автомат або персональна ЕОМ, що вміє реалізувати скінчений набір дій. Наказ на виконання дії з вказаного набору, що виражається певним, раніше обумовленим способом, називається розпорядженням, а вся сукупність допустимих наказів – системою розпорядження виконавця.
Даючи завдання виконавцю на деяку роботу, ми звичайно даємо йому не одне розпорядження, а деяку скінчену послідовність розпоряджень, задаючи також порядок, у якому вони виконуються. Така послідовність розпоряджень з вказанням порядку їх виконання називається програмою.
Саме програма на певній мові програмування є вхідним даним для компілятора, що у союзі з відлагоджувачем перекладає послідовність команд у спеціальні машинні коди і здійснює виконання вхідної послідовності команд запуском відповідного виконавчого файлу.
Зміст
Завдання на проектування.................................................................................4
Вступ.......................................................................................................................5
Огляд способів та методів побудови компіляторів........................................6
Лексичний аналіз............................................................................................6
Синтаксичний аналіз......................................................................................6
Семантичний аналіз.......................................................................................7
Генерація коду................................................................................................7
Аналіз завдання та розробка проекту задачі...................................................8
Опис граматики вхідної мови.............................................................................9
Деталізований опис вхідної мови..................................................................9
Перелік термінальних символів та ключових слів.......................................9
Формальний опис вхідної мови в термінах BNF.........................................9
Формальний опис заданої вхідної мови в термінах BNF...........................10
Повне дерево граматичного розбору............................................................11
Розробка алгоритмів складових компілятора та вибраних структур
даних........................................................................................................................12
Блок-схема лексичного аналізатора .............................................................13
Блок-схема синтаксичного аналізатора........................................................14
Блок-схема генератора коду...........................................................................15
Блок-схема інтегрованого середовища.........................................................16
Опис інтерфейсу користувача та результати тестування розроблених
програм....................................................................................................................17
Робоча програма ST1.txt.................................................................................17
Робоча програма ST2.txt.................................................................................19
Висновки
Список літератури
Додаток
Завдання на проектування
Розробити компілятор вхідної мови програмування , короткий опис якої подано нижче:
блок тіла програми: Begin – end
оператори вводу/виводу: read – write
умовний оператор: if – then
оператор циклу: for – to – do
оператор присвоєння: >>
знаки операції: +, -, /, *, ^, %, =, !=, >, <, >=, <=, !, &, |
ключові слова: L
ідентифікатори: U4
коментері: \\ …
типи даних: int, boolean, double, string
Після успішного виконання лексичного та синтаксичного аналізу, здійснити генерацію коду, відкомпілювати і запустити його на виконання.
2.Вступ
Компілятори становлять істотну частину програмного забезпечення ЕОМ. Це пов’язано з тим, що мови високого рівня стали основним засобом розробки програм. Тільки дуже незначна частина програмного забезпечення, що вимагає особливої ефективності, програмується за допомогою асемблерів. На сьогодні існує досить багато мов програмування. Нарівні з традиційними мовами, такими, як Fortran, широке поширення отримали так звані «універсальні мови» (Паскаль, Сі, Модула-2, Ада та інші), а також деякі спеціалізовані (наприклад, мова обробки облікових структур Лісп). Крім того, велике поширення отримали мови, пов’язані з вузькими предметними областями, такі, як вхідні мови пакетів прикладних програм.
Для деяких мов є досить багато реалізацій. Наприклад, реалізацій Паскаля, Модули-2 або Сі для ЕОМ типу IBM/PC на ринку десятки. З іншого боку, постійно зростаюча потреба в нових компіляторах пов’язана з бурхливим розвитком архітектури ЕОМ. Цей розвиток йде у різних напрямах. Удосконалюється стара архітектура, як в концептуальному відношенні, так і по окремих, конкретних лініях. Це можна проілюструвати на прикладі мікропроцесора Intel 80х86. Послідовні версії цього мікропроцесора 8086, 80186, 80286, 80386, 80486, 80586 відрізняються не тільки технічними характеристиками, але і, що більш важливо, новими можливостями і, значить, зміною (розширенням) системи команд. Отже, це вимагає нових компіляторів (або модифікації старих).
Також бурхливо розвивається різна паралельна архітектура – векторні системи, багатопроцесорні системи, а також системи з широким командним словом (VLIW), варіантом яких є суперскалярні ЕОМ. На ринку вже є десятки типів ЕОМ з паралельною архітектурою, починаючи від супер-ЕОМ (Cray, CDC та інші), через робочі станції (наприклад, IBM/RS-6000) і закінчуючи персональними (наприклад, на основі мікропроцесора і860). Отже, для кожної з машин створюються нові компілятори для багатьох мов програмування. Тут необхідно також відмітити, що нова архітектура вимагає розробки абсолютно нових підходів до створення компіляторів, так що поряд з розробкою компіляторів ведеться і велика наукова робота по створенню нових методів трансляції.
Огляд способів та методів побудови компіляторів
3.1 Лексичний аналіз
Основна задача лексичного аналізу – розбити вхідний текст, що складається з послідовності одиночних символів, на послідовність слів, або лексем, тобто виділити ці слова з безперервної послідовності символів. Всі символи вхідної послідовності з цієї точки зору розділяються на символи, що належать яким-небудь лексемам, і символи, що розділяють лексеми.
Звичайно всі лексеми діляться на класи. Прикладами таких класів є числа, ідентифікатори, рядки. Окремо виділяються ключові слова і символи пунктуації (іноді їх називають символи-обмежувачі). Як правило, ключові слова – це деяка кінцева підмножина ідентифікаторів. З точки зору подальших фаз аналізу лексичний аналізатор видає інформацію двох типів: для синтаксичного аналізатора, працюючого услід за лексичним, істотна інформація про послідовності класів лексем, обмежувачів та ключових слів, а для семантичного аналізу, працюючого услід за синтаксичним, важлива інформація про конкретні значення окремих лексем (ідентифікаторів, чисел і т.д.). Тому загальна схема роботи лексичного аналізатора така. Спочатку виділяється окрема лексема. Якщо виділена лексема – обмежувач, то він (точніше, деяка його ознака) видається як результат лексичного аналізу. Ключові слова розпізнаються або явним виділенням безпосередньо з тексту, або спочатку виділяється ідентифікатор, а потім робиться перевірка на приналежність його до ключових слів. Якщо так, то видається ознака відповідного ключового слова, якщо немає – видається ознака ідентифікатора, а сам ідентифікатор зберігається окремо. Якщо виділена лексема належить якому-небудь з інших класів лексем (число, рядок і т.д.), то видається ознака класу лексеми, а значення лексеми зберігається.
Лексичний аналізатор може працювати або як самостійна фаза трансляції, або як підпрограма, працююча за принципом «дай лексему». У першому випадку виходом лексичного аналізатора є файл лексем, у другому лексема видається при кожному зверненні до лексичного аналізатора (при цьому, як правило, тип лексеми повертається як значення функції, а значення передається через глобальну змінну). З точки зору формування значень лексем, належних класам лексем, лексичний аналізатор може або просто видавати значення кожної лексеми і в цьому випадку побудова таблиць переноситься на більш пізні фази, або він може самостійно будувати таблиці об'єктів (ідентифікаторів, рядків, чисел і т.д.). У цьому випадку, як значення лексеми видається покажчик на вхід у відповідну таблицю.
Робота лексичного аналізатора описується формалізмом кінцевих автоматів. Однак безпосередній опис кінцевого автомата незручний практично. Тому для опису лексичних аналізаторів, як правило, використовують або формалізм регулярних виразів, або формалізм контекстно вільних граматик. По опису лексичного аналізатора у вигляді регулярних виразів або автоматної граматики будується кінцевий автомат, що розпізнає відповідну мову.
Синтаксичний аналіз
Синтаксичний аналіз – це процес, в якому досліджується таблиця лексем, або кожна окрема лексема, яку повернув лексичний аналізатор, і визначається, чи відповідає вона структурним умовам, що явно сформульовані у визначені синтаксису мови. Синтаксичний аналізатор сприймає вихід лексичного аналізатора і розбирає його у відповідності до граматики вихідної мови. Але, в граматиці вихідної мови, як правило не вказується, які конструкції слід вважати лексемами. Прикладами конструкцій, які як правило розпізнаються на фазі лексичного аналізу, можуть бути ключові слова, константи та ідентифікатори. Але ці ж самі конструкції можуть розпізнаватися і синтаксичним аналізатором. На практиці не існує конкретного правила, що визначає, які конструкції повинні розпізнаватися на фазі лексичного аналізу, а які на фазі синтаксичного. Як правило, це визначає розробник компілятора, виходячи з технологічних аспектів програмування, а також синтаксису та семантики вхідної мови.
Синтаксичний аналізатор – це частина компілятора, яка відповідає за виявлення основних синтаксичних конструкцій вихідної мови. В задачу синтаксичного аналізатора входить: знайти та виділити основні синтаксичні конструкції мови в тексті програми, встановити тип та правильність кожної синтаксичної конструкції і представити синтаксичні конструкції у вигляді, зручному для подальшої генерації тексту результуючої програми. Як правило, синтаксичні конструкції мови програмування можуть бути описані за допомогою контекстно-вільних граматик. Розпізнавач дає відповідь на питання, належить чи ні, ланцюжок вхідних символів заданій мові. Але, задача синтаксичного аналізу, не обмежується тільки такою перевіркою. Синтаксичний аналізатор повинен мати, деяку результуючу мову, за допомогою якої він передає наступним фазам компіляції, інформацію про знайдені і розібрані синтаксичні конструкції.
Семантичний аналіз
Семантичний аналіз – це перевірка вхідної програми на правильне використання ідентифікаторів. А саме: використання тільки задекларованих попередньо ідентифікаторів, їх ініціалізація перед використанням у виразах, сумісність між різними типами ідентифікаторів, а також перевірка цифрових констант на переповнення.
В даній курсовій роботі семантичний аналізатор вбудований в генератор коду, тобто під час генерування коду, здійснюється перевірка семантичних правил, щодо виразів і числових констант.
Генерація коду
Задача генератора коду – побудова еквівалентної машинної програми по програмі на вихідній мові. Звичайно як вхідний для генератора код служить деяке проміжне представлення програми. У свою чергу, генерація коду складається з ряду специфічних, відносно незалежних підзадач: розподіл пам’яті (зокрема, розподіл регістрів), вибір команд, генерація об’єктного (або завантажувального) модуля. Звичайно, незалежність цих підзадач відносна: наприклад, при виборі команд не можна не враховувати схему розподілу пам’яті, і навпаки, схема розподілу пам’яті (зокрема регістрів) веде до генерації тієї або іншої послідовності команд. Однак зручно і практично ці задачі все ж таки розділяти і при цьому особливо звертати увагу на їх взаємодію.
В певній мірі схема генератора коду залежить від форми проміжного представлення. Зрозуміло, що генерація коду з дерева відрізняється від генерації коду з трійок, а генерація коду з префіксної форми запису відрізняється від генерації коду з орієнтованого графа. У той же час всі генератори коду мають і багато загального і основні вживані алгоритми відрізняються, як правило, тільки в деталях, пов'язаних з проміжним представленням, що використовується.
Головна мета етапу: створити оптимізований код для виконання (запуску) програми завантажувачем. Для оптимізації швидкодії критичні участки програми пишуться на нижчих мовах програмування. Це було можливим до тієї пори, поки Інтел не розробили нові сучасні процесори. Так як в сучасних процесорах програміст не в стані ефективно передбачити в якому порядку виконуватимуться інструкції, а тому він не в стані писати оптимізований код. За нього це має робити і робить оптимізуючий компілятор.
4. Аналіз завдання та розробка проекту задачі
До розробки компілятора заданої вхідної мови програмування висуваються наступні вимоги:
Кожна програма починається зі слова begin і закінчується словом end. Все що до begin і після end не аналізується. Наприклад як у мові Паскаль begin end.
Програма має надавати можливість працювати зі змінними таких типів:
Int – цілі числа;
Boolean – логічні;
Double – дійсні числа;
String – рядки символів.
Змінні перед використанням мають бути попередньо оголошені за наступним форматом: “тип даних” “змінна”. Наприклад: integer x;
Присвоєння до змінних виконується оператором присвоєння >>. Наприклад: x >> y + 5;
Над змінними виконуються наступні операції:
Арифметичні:
+,-,*,/ додавання, віднімання, множення, ділення;
^ піднесення до степеня;
% обчислення залишку;
Логічні:
==,!= рівність, нерівність;
>,<,>=,<= більше, менше, більше-рівне, менше-рівне;
!,&,| логічні операції NOT, AND, OR.
Ввід даних зі стандартного вводу відбувається оператором read, а вивід оператором write. Наприклад: read x; write y.
Для виконання умовних дій використовується оператор if-then. Наприклад: if ( a > b ) then
a >> b;
Програмування циклів забезпечується оператором for-to-do. Наприклад: for ( i >> 1 to 5)
< оператори > do.
Цільова мова компілятора ANSI C. Мова розробки компілятора: ANSI C.
Потрібно також розробити інтегроване середовище, яке б включало текстовий редактор з можливістю набору, редагування програми; зчитування та запису її з/на диск, запуску трансляції.
На вхід розробленого компілятора має подаватися текстовий файл, написаний на заданій мові програмування. На виході розробленого компілятора мають з’являтися три файли:
файл з результатом лексичного аналізу,
файл з результатом синтаксичного аналізу,
файл з результуючим файлом на мові С.
Для захисту курсового проекту потрібно мати:
пояснювальну записку,
файл з вихідним текстом компілятора на мові ANSI C або C++,
виконавчий файл компілятора,
набір тестових програм та вхідній мові програмування.
5. Опис граматики вхідної мови
Одною з перших задач, що виникають при побудові компілятора, є визначення вхідної мови програмування. Для цього використовують різні способи формального опису. В даній курсовій роботі використано розширену нотацію Бекуса-Наура (Backus/Naur Form - BNF).
5.1 Деталізований опис вхідної мови
Типи даних: integer, boolean, double, string;
Арифметичні операції над числами: +, - , *, /, ^, %;
Логічні операції : ==, !=, >, <, >=, <=, !, &, |;
Оператор присвоєння значень змінним : >> ;
Оператори вводу/виводу: read – write ;
Позначення початку і кінця програми: begin – end ;
Оператор циклу: for – to – do ;
Умовний оператор: if – then .
Перелік термінальних символів та ключових слів
Визначимо окремі термінальні символи та нерозривні набори термінальних символів (ключові слова):
Begin <=end ==; !=>> !+ &- |* if
/ then
^ for
% to
( do) read< write>>=
До термінальних символів віднесемо також усі цифри (0-9), латинські букви (a-z) та символ пробілу (“ ”).
Всього: 28 + 10 + 26 + 1 = 65 термінальних виразів.
5.3 Формальний опис вхідної мови в термінах BNF
Правила написання правил у розширеній нотації Бекуса-Наура: - нетермінальні вирази записуються у кутових дужках: “<”, “>” ;- термінальні вирази записуються жирним шрифтом або у подвійних лапках;- усі нетермінальні вирази мають бути “розкриті” за допомогою термінальних;- сивол “::=” відділяє праву частину правила від лівої;- символ “|” розділяє альтернативи;- сиволи “[”, “]” означають необов’язковість (вираз в дужках може бути відсутнім);- сиволи “{”, “}” означають повторення.
5.4 Формальний опис заданої вхідної мови в термінах BNF
<program>
::= begin < statement > [{<statement >}] end
<statement>
::= <declaration> | <operator>
<declaration>
::= <type> <var>;
<operator>
::= <assign> | <command>
<assign>
::= <var> >> <expression>;
<var>
::= <letter> | [{<letter>}].
<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
<type>
::= integer | boolean | double | string
<command>
::= <condition> | <loop> | <input> | <output>;
<condition>
::= if ( <bool_expr> ) then <operator>
<loop>
::= for ( <var> >> <int_const> to <int_const> ) <operator> | [{<operator>}] do
<int_const>
::= <цифра> | [{<цифра >}]
<real_const>
::= <digit> | [{<digit>}].<digit> | [{<digit>}]
<char_const>
::= ‘<letter>’
<str_const>
::= “<letter> | [{<letter>}]”
<digit>
::= 0|1|2|3|4|5|6|7|8|9
<expression>
::= <num_expr> | <bool_expr>
<num_expr>
::= <num_operand> [{<num_operation><num_operand>}]
<bool_expr>
::= (<num_operand><bool_operation><num_operand>) | <var>
<num_operand>
::= (<num_expr>) | <var> | <int_const> | <real_const> | <char_const> | <str_const>
<num_operation>
::= + | - | * | / | ^ | %
<bool_operation>
::= == | != | > | < | >= | <= |! | &| |
<input>
::= read <var>
<output>
::= write <var>
5.5 Повне дерево граматичного розбору
6. Розробка алгоритмів складових компілятора та вибраних структур даних
Для розробки вхідної мови програмування можна виділити сім різних логічних задач:
Інтерпретація – визначення точного змістового навантаження, створення матриці та таблиць з допомогою програм інтерпретації;
Машинно-незалежна оптимізація – створення оптимальнішої матриці;
Лексичний аналіз – розпізнавання базових елементів та створення стандартних символів;
Синтаксичний аналіз – розпізнавання базових синтаксичних конструкцій з використан- ням редукцій;
Розподіл пам’яті – модифікація таблиць ідентифікаторів та літералів. В матриці розміщується інформація, з допомогою якої генерується код, який розподіляє динамічну пам’ять;
Генерація коду – використання макропроцесора для отримання більш оптимального вихідного коду;
Компонування кінцевого вихідного коду – розв’язання проблеми символічних адрес та генерування програми на машинній мові.
Фази з першої по четверту машинно-незалежні і визначаються тільки мовою. Фази з п’ятої по сьому – машинно-залежні і не залежать від мови. З метою забезпечення вищої ефективності ці фази можуть не розділятись так чітко.
Компілятор потрібно оцінювати не тільки по об’єктному коду, що генерується, але також і по об’єму оперативної пам’яті, що він займає і по часу, що потрібен для трансляції. На жаль, ці критерії оптимальності часто досить суперечливі. Крім того, оптимальність коду обернено пропорційна складності, розміру та часу роботи самого компілятора.
Компілятором використовуються бази даних, що забезпечують зв’язки між фазами: вхідний код, таблиця стандартних символів, таблиця термінальних символів, таблиця ідентифікаторів, таблиця літералів, редукції, матриця, кодові продукції, код компоновки, кінцевий об’єктний код.
6.1 Блок-схема лексичного аналізатора
6.2 Блок-схема синтаксичного аналізатора
6.3 Блок-схема генератора коду
6.4 Блок-схема інтегрованого середовища
7. Опис інтерфейсу користувача та результати розроблених програм
7.1 Робоча програма ST1.txt
Текст програми
begin
integer i;
boolean b;
string s;
double r;
read i;
r >> i / 7.25;
for ( i >> 1 to 5 )
read s;
do;
if ( r == i ) then s >> "Stev";
write r;
end
Результат лексичного аналізу
begin
integer
i
;
boolean
b
;
string
s
;
double
r
;
read
i
;
r
>>
i
/
7.25
;
for
(
i
>>
1
to
5
)
read
s
;
do
;
if
(
r
==
i
)
then
s
>>
"Stev"
;
write
r
;
end
Результат синтаксичного аналізу
Syntax analisys
begin [Start-Begin]
integer [Block-Decl]i ; [Declaration – Semicolon]
boolean [Block-Decl]b ; [Declaration – Semicolon]
string [Block-Decl]s ; [Declaration – Semicolon]
double [Block-Decl]r ; [Declaration – Semicolon]
read [Block – Read]i [Read – Iden]; [Block – Semicolon]
r [Block – Iden]>> [Block – Iden]i [Block – Iden]/ [Block – Iden]7.25 [Block – Literal]; [Block – Semicolon]
for [Block – For]( [For – OpenBr]i [For params – Iden]>> [For params – Iden]1 [For param – Literal]to [For param – Iden]5 [For param – Literal]) [For param – CloseBr]
read [Block – Read]s [Read – Iden]; [Block – Semicolon]
do [Block – Do]; [Block – Semicolon]
if [Block – If]( [If – OpenBr]r [If condition – Iden]== [If condition – Iden]i [If condition – Iden]) [If condition – CloseBr]
then [Block – Then]s [Block – Iden]>> [Block – Iden]”Stev” [Block – Literal]; [Block – Semicolon]
write [Block – Write]r [Block – Iden]; [Block – Semikolon]
end [Block – End]
Результат генерації коду
#include <stdio.h>
void main(){
integer i;
integer b;
char *s;
float r;
scanf("%d",i);
r=i/7.25;
for(i=1;i<=5;i++)scanf("%s",s);
if(r==i)s="Stev";
printf("%f",r);}
7.2 Програма з помилкою ST2.txt
Текст програми
begin
integer i;
boolean b;
string s;
double r;
read i;
r << i / 7.25;
for ( i >> 1 to 5 )
read s;
do;
if ( r == i ) then s >> "Stev";
out r;
write
Результат лексичного аналізу
begin
integer
i
;
boolean
b
;
string
s
;
double
r
;
read
i
;
r
<<
i
/
7.25
;
for
(
i
>>
1
to
5
)
read
s
;
do
;
if
(
r
==
i
)
then
s
>>
"Stev"
;
write
r
;
end
Результат синтаксичного аналізу
Syntax analisys
begin [Start - Begin]
integer [Block - Decl]i ; [Declaration- Semicolon]
boolean [Block - Decl]b ; [Declaration- Semicolon]
string [Block - Decl]s ; [Declaration- Semicolon]
double [Block - Decl]r ; [Declaration- Semicolon]
read [Block - Read]i [Read-Iden]; [Block- Semicolon]
r [Block- Iden]<< [Block- Iden]
Wrong indentifier
Error in syntax analiz
Висновки
В процесі виконання курсової роботи було виконано наступне:
Складено формальний опис мови програмування в термінах розширеної нотації Бекуса-Наура, виділено усі термінальні символи та ключові слова.
Створено компілятор мови програмування , а саме:
Розроблено прямий лексичний аналізатор, орієнтований на розпізнавання лексем, що є заявлені в формальному описі мови програмування.
Розроблено синтаксичний аналізатор на основі автомата з магазинною пам’яттю. Складено таблицю переходів для даного автомата згідно правил записаних в термінах Бекуса-Наура.
Розроблено засоби перевірки семантичних правил, що використовуються в генераторі коду. Дані засоби дозволяють виявляти такі помилки як: використання неоголошених змінних, подвійне оголошення ідентифікаторів та ін.
Розроблено генератор коду, відповідні процедури якого викликаються після перевірки синтаксичним аналізатором коректності запису чергового оператора мови програмуваня . Вихідним кодом генератора є програма на мові C.
Проведене тестування компілятора на тестових програмах за наступними пунктами:
На виявлення лексичних помилок.
На виявлення синтаксичних помилок.
На виявлення семантичних помилок.
Загальна перевірка роботи компілятора.
Тестування не виявило помилок в роботі компілятора і всі помилки в тестових програмах на мові програмуваня були успішно виявлені і відповідно оброблені.
В результаті виконання даної курсової роботи було успішно засвоєно методи розробки та реалізації компонент системного програмного забезпечення.
Список літератури
Прата С. Язык программирования С++. – К.: DiaSoft, 2000
Шильдт Г. С++. – Санкт-Петербург: BXV, 2002. – 688 с.
Хантер Р. Проектирование и конструирование компиляторов. – М.: Финансы и статистика, 1984. – 232 с.
Шамис В. А. С++Builder 4 Техника визуального программирования. – М.: Нолидж, 2000. – 656 с.
Ахо А., Ульман Дж. Теория синтаксического анализа, перевода и компиляции. – М.: Мир, 1978. – т.1, 612 с.
Серебряков В.А. Лекции по конструированию компиляторов. – М.: МГУ, 1993.
В.С. Проценко, П.Й. Чаленко, А.Б.Ставровський “Техніка програмування мовою Сі”.-Київ “Либідь”, 1993. – 224с.
Б.Керниган, Д.Ритчи “Язык программирования Си”. – Москва “Финансы и статистика”, 1992. – 271с.
Н.Г.Голубь “Исскуство программирования на асемблере.Лекции и упражнения”. –Киев “DiaSoft”, 2002 – 642с.
10. Л.Бэк “Введение в системное програмирование”. – М.: Мир, 1999
Додаток
#include <iostream>
#include <iomanip>
#include <cmath>
#include <cstdlib>
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <conio.h>
#define SEPARATORS " \t\n\r"
#define TRUE 1
#define FALSE 0
#define MAX_SRC_SIZE 4096
#define WHITE 0
#define YELLOW 1
#define BLUE 2
#define RED 3
#define GREEN 4
#define BLACK 5
#define GREY 6
FILE *fSAin;
FILE *fsyn;
FILE *fcode;
char sFname[255]=""; // Вихідний файл редактора
char *sCurStr;
int nFor=0; // Кількість For
int nDo=0; // Кількість Do
char *KeyWords[]={"begin","end","read","write","if","then","for",
"integer","boolean","double","string","+","-","*","/","%","^","==","!=",
"<",">","<=",">=","!","&","|",";",">>","do",
"(",")","to",NULL};
struct TNameTable {
char name[16];
char type[16];
char replace[16];
};
// Таблиця ідентифікаторів
TNameTable NameTable[256]={
{"begin","",""},{"end","",""},{"read","",""},{"write","",""},{"if","",""},{"then","",""},
{"for","",""},{"integer","",""},{"+","",""},{"-","",""},{"*","",""},{"==","",""},
{"!=","",""},{"<","",""},{">","",""},{"<=","",""},{">=","",""},{"!","","!!"},
{"&","","&&"},{"|","","||"},{"/","","/"},{"%","","%"},{"^","","^"},{">>","","="},
{"to","",""},{"(","",""},{")","",""},{"do","",""}
};
int NTCount=29;// К-ть записів таблиці імен
//**************************************************************
#include "console.h"
using namespace std;
POINT screensize;
//**************************************************************
// Очистка екрану
//**************************************************************
void clrscr()
{
COORD coordScreen = { 0, 0 };
DWORD cCharsWritten;
CONSOLE_SCREEN_BUFFER_INFO csbi;
DWORD dwConSize;
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
GetConsoleScreenBufferInfo(hConsole, &csbi);
dwConSize = csbi.dwSize.X * csbi.dwSize.Y;
screensize.x = csbi.dwSize.X;
screensize.y = csbi.dwSize.Y;
FillConsoleOutputCharacter(hConsole, TEXT(' '), dwConSize, coordScreen, &cCharsWritten);
GetConsoleScreenBufferInfo(hConsole, &csbi);
FillConsoleOutputAttribute(hConsole, csbi.wAttributes, dwConSize, coordScreen, &cCharsWritten);
SetConsoleCursorPosition(hConsole, coordScreen);
}
//**************************************************************
// Порівняння двох стрічок
//**************************************************************
int cmp(char *a, char *b) {
if (!strcmp(a,b)) return 1; else return 0;
}
//**************************************************************
// Встановити координати курсора x, y у консольному вікні
//**************************************************************
void gotoxy(int x, int y) {
COORD point;
// if((x < 0 || x > screensize.x) || (y < 0 || y > screensize.y)) return;
point.X = x; point.Y = y;
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), point);
}
//**************************************************************
// Встановити колір тексту
//**************************************************************
void textcolor(int color) {
switch (color) {
case WHITE:SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_INTENSITY |
FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);break;
case RED:SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_INTENSITY |
FOREGROUND_RED);break;
case GREEN:SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_INTENSITY |
FOREGROUND_GREEN);break;
case YELLOW:SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_INTENSITY |
FOREGROUND_RED | FOREGROUND_GREEN);break;
case BLUE:SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_INTENSITY |
FOREGROUND_BLUE);break;
case GREY:SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),FOREGROUND_INTENSITY |
FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);break;
}
}
//**************************************************************
// Виводить повідомлення про помилку
//**************************************************************
void error(char *s) {textcolor(GREY);cprintf("\n\r%s\n\r",s);textcolor(BLACK);}
//**************************************************************
// Очистити вікно і вивести назву
//**************************************************************
void setwin(char *s){textcolor(WHITE);clrscr();puts(s);
gotoxy(0,24);
cputs("Press any key to continue");gotoxy(0,1);}
//**************************************************************
// Вивести у результуючий файл потрібну інструкцію
//**************************************************************
void code(char s[255]) {fprintf(fcode,s);fflush(fcode);}
//**************************************************************
// Заголовки обробників подій
//**************************************************************
int hNull(int event, char *data);
int hBlock(int event, char *data);
int hDo(int event, char *data);
int hNothing(int event, char *data);
int hProgExp(int event, char *data);
int hEndExp(int event, char *data);
int hBegin(int event, char *data);
int hDecl(int event, char *data);
int hIden(int event, char *data);
int hFor(int event, char *data);
int hForParams(int event, char *data);
int hIf(int event, char *data);
int hIfCond(int event, char *data);
int hWrite(int event, char *data);
int hRead(int event, char *data);
int hEndProg(int event, char *data);