Створення компілятора
Вивчення складових частин, основних принципів побудови і функціонування компіляторів. Поняття хешування, сутність алгоритму роботи лексичного аналізатора. Практичне освоєння методів побудови простих компіляторів для заданої вхідної мови - Borland Delphi.
Рубрика | Программирование, компьютеры и кибернетика |
Вид | дипломная работа |
Язык | украинский |
Дата добавления | 27.05.2013 |
Размер файла | 763,6 K |
Отправить свою хорошую работу в базу знаний просто. Используйте форму, расположенную ниже
Студенты, аспиранты, молодые ученые, использующие базу знаний в своей учебе и работе, будут вам очень благодарны.
Размещено на http://www.allbest.ru/
3
Размещено на http://www.allbest.ru/
КУРСОВА РОБОТА
з дисципліни: “Системне програмне забезпечення”
На тему: “Створення компілятора”
ЗМІСТ
- Вступ 3
- 1. Короткі теоретичні відомості 4
- 1.1 Таблиці ідентифікаторів 4
- 1.1.1 Метод бінарного дерева 5
- 1.1.2 Хешування 6
- 1.2 Лексичний аналізатор 7
- 1.3 Синтаксичний аналізатор 9
- 1.4 Генерація об'єктного коду 11
- 2. Таблиця передування 13
- 3. Лістинг програм 15
- 4. Результати роботи 144
- Висновки 147
- Перелік використаної літератури 148
Вступ
Тема: Створення компілятора
Мета роботи: вивчення складових частин, основних принципів побудови і функціонування компіляторів. Практичне освоєння методів побудови простих компіляторів для заданої вхідної мови.
Завдання
Для програмної організації компілятора рекомендується використовувати Borland Delphi:
Компілятор повинен складатися:
1) лексичний аналізатор
2) синтаксичний аналізатор
3) оптимізатор
4) генератор результуючого кода
Для побудови компілятора бажано використовувати методи застосовані при виконанні лабораторної роботи.
Варіант - 5
1) Тип констант: вісімкові
2) Додаткові операції: >><<
3) Оператори цикла: repeat <оператор> until <вираз>
4) Оптимізація: виключення зайвих операцій
5) Тип даних: integer
6) Тип коментарів: {коментарій}
1. Короткі теоретичні відомості
1.1 Таблиці ідентифікаторів
Способи організації таблиць ідентифікаторів:
1) прості і впорядковані списки
2) бінарне дерево
3) хеш-адресація з ре хешуванням
4) хеш-адресація за методом ланцюжків
5) комбінація хеш-адресації зі списком або бінарним деревом
Задача компілятора в тому, щоб зберігати інформації про кожний елемент початкової програми і ати доступ до цієї інформації по імені елемента. Для рішення цієї задачі служать таблиці ідентифікаторів. Таблиця складається з набору полів даних (записів) кожне з яких відповідає одному елементу початкової програми.
Компілятор поповнює записи таблиці ідентифікаторів по мірі аналізу початкової програми із знаходженням в ній нових елементів, яки вимагають розміщення в таблиці. Пошук інформації в таблиці виконується всякий раз коли компілятору необхідні дані про той або інший елемент програми. Пошук елементів в таблиці виконується компілятором набагато частіше ніж запис. Кожному додаванню елемента в таблицю передує пошук, щоб пересвідчитися що немає такого елемента в таблиці. На кожну операції пошуку елемента в таблиці компілятор витрачає час і ,оскільки число елементів в початковій програмі може біти від одного до сотень тисяч, цей час буде істотно впливати на загальний час компіляції. Тому пошук має бути максимально швидким.
1.1.1 Метод бінарного дерева
В цьому методі кожний вузол дерева є елементом таблиці, кореневим вузлом стає перший елемент, який компілятор зустрів при заповненні таблиці. Дерево є бінарним, бо кожна вершина має не більше 2 гілок.
Компілятор працює з потоком вхідних даних, що містить ідентифікатори.
Алгоритм роботи бінарного дерева:
1) Перший - заноситься в вершину дерева, наступні попадають в дерево за таким алгоритмом:
2) 1) вибрати черговий ідентифікатор з потоку, якщо чергового нема, то побудова закінчена
3) зробити поточним вузлом дерева кореневу вершину
4) порівняти ім'я чергового ідентифікатора з іменем ідентифікатора який міститься в поточному вузлі
5) якщо ім'я чергового ідентифікатора менше - перейти до кроку 5, якщо дорівнює - зупинити алгоритм, оскільки однакових ідентифікаторів бути не повинно, інакше - перейти до кроку 7
6) якщо у поточного вузла існує ліва вершина, то зробити її поточним вузлом и повернутися до кроку 3, інакше - до кроку 8
7) створити нову вершину, помістити в неї інформацію про черговий ідентифікатор, зробити цю нову вершину лівою вершиною поточного вузла і перейти до кроку 1
8) якщо у поточного вузла є права вершина, то зробити її поточним вузлом і повернутися до кроку 3, інакше - до кроку 8
9) створити нову вершину, помістити в неї інформацію про черговий ідентифікатор, зробити нову вершину правою вершиною поточного вузла і перейти до кроку 1
Пошук називається бінарним тому, що на кожному кроці об'єм розгляданої інформації скорочується в 2 рази, тобто шуканий символ порівнюється з (n+1)/2 елементами в середині таблиці, якщо співпадінь немає, то переглядається блок елементів пронумерованих від 1 до (n+1)/2-1 або від (n+1)/2 до n в залежності від того менше чи більше шуканий елемент порівнюваного, далі процес повторюється над блоком вдвічі меншого розміру.
Число порівнянь і форма дерева залежить від порядку надходження ідентифікаторів.
Недоліком також є необхідність зберігати 2 додаткові посилання на ліву и праву гілки в кожному елементі дерева і робота з динамічним виділенням пам'яті для побудови дерева.
1.1.2 Хешування
Для хешування використовується поняття хеш-функції і хеш-адресації. Хеш-функція F відображає множини вхідних елементів R на множину цілих невід'ємних чисел Z
R->Z:F(r) = n, r є R, n є Z
Множина допустимих вхідних елементів називається областю визначення функції F.
Множина значень хеш-функції - M є Z
Процес відображення області визначення хеш-функції на множину значень - хешування, тобто відображення імен ідентифікаторів на множину цілих невід'ємних чисел. Хеш-адресація полягає у використанні значення, яке видає хеш-функція в якості комірки з деякого масиву даних.
Метод хешування дуже ефективний тому, що час розміщення елемента в таблиці і час пошуку елемента в таблиці визначається лише часом обчислення хеш-функції, який є на порядки меншим необхідного для багатократних порівнянь елементів в таблиці.
При хешуванні кожний елемент таблиці записується в комірку, адресу якої видає хеш-функція обчислена для цього елемента. Для занесення будь-якого елемента в таблицю ідентифікаторів треба обчислити його хеш-функцію і звернутися до потрібної комірки масиву даних. Для пошуку елемента в таблиці треба обчислити функцію для шуканого елемента і перевірити чи не є задана нею комірка масиву пустою, якщо комірка не пуста - знайдено.
Недоліки:
- неефективне використання об'єму пам'яті оскільки пам'ять повинна виділятися під всю область значень хеш-функції, тоді як реально зберігається набагато менше.
- необхідність прийнятного вибору хеш-функції
1.2 Лексичний аналізатор
Лексичний аналізатор - це частина компілятора, яка читає літери програми на вхідній мові і будує з них слова (лексеми) початкової мови. На вхід лексичного аналізатора надходить текст початкової програми, а інформація на виході передається синтаксичному аналізатору.
Лексема - структурна одиниця мови, яка складається з символів мови і не містить в своєму складі інших структурних одиниць мови. Лексемами є ідентифікатори, константи, ключові слова, знаки операції.
Етап лексичного аналізу програми введено за таких причин:
- спрощення робота з текстом початкової програми і скорочення об'єму інформації, що надходить, оскільки лексичний аналізатор структурує інформацію, що надходить і вилучає непотрібну інформацію
- для виділення і розбору лексем можливо застосовувати легку і ефективну техніку аналізу
- лексичний аналізатор відділяє конструктивно складний синтаксичний аналіз від роботи з текстом початкової програми, це дає змогу при переході від однієї версії мови до іншої внести незначні зміни в простий лексичний аналізатор
Таблиця лексем
Результатом роботи лексичного аналізатора є перелік всіх знайдених в початковій програмі лексем і додаткової службової інформації. Таблиця лексем в кожному рядку містить інформацію про вид лексеми, її тип і значення. Як правило в цій таблиці є 2 поля: тип лексеми, вказівник на інформацію про лексеми. Не слід плутати таблицю лексем з таблицею ідентифікаторів тому, що таблиця лексем фактично містить весь текст початкової програми обробленої лексичним аналізатором.
В таблиці лексем будь-яка лексема може зустрічатися будь-яке число раз, тоді як в таблиці ідентифікаторів кожна лексема зустрічається лише один раз. До того ж таблиця ідентифікаторів містить лише ідентифікатори і константи, а інших типів лексем в ній немає.
Лексеми обов'язково розміщені в тому ж порядку, що і в початковій програмі, а в таблиці ідентифікаторів лексеми розміщені в порядку зручному для пошуку.
Алгоритм роботи лексичного аналізатора:
1) переглядається вхідний потік символів програми на початковій мові до знаходження чергового символу, який обмежує лексеми;
2) для вибраної частини вхідного потоку відбувається розпізнавання лексеми;
3) при успішному розпізнаванні інформація про виділену лексему заноситься в таблицю лексем і алгоритм повертається на перший крок;
4) при неуспішному розпізнаванні видається сповіщення про помилку і наступні дії залежать від реалізації лексичного аналізатора: або його виконання зупиняється або робиться спроба розпізнати наступну лексему тобто крок 1;
Робота програми лексичного аналізатора продовжується до тих пір, поки не будуть переглянуті всі символи програми на початковій мові з вхідного потоку.
1.3 Синтаксичний аналізатор
компілятор хешування лексичний аналізатор
За ієрархією граматик Хамського є 4 головні групи мов і граматик, що їх описують. Регулярні і контекстно-вільні граматики використовуються при описі синтаксису мов програм. З допомогою регулярних граматик описуються лексеми мови, а саме ідентифікатори, константи, службові слова та ін. А на основі контекстно-вільних граматик будуються більш складні синтаксичні конструкції: опис типів і змінних, арифметичні і логічні вирази, керуючі оператори і повністю вся програма на вхідній мові.
Синтаксичний аналізатор - частина компілятора, яка відповідає за виявлення і перевірку синтаксичної конструкції вхідної мови. В задачу синтаксичного аналізатора входить:
1) знайти і виділити синтаксичні конструкції в тексті початкової програми;
2) встановити тип і перевірити правильність кожної синтаксичної конструкції;
3) представити синтаксичні конструкції в виді зручному для наступної генерації результуючої програми;
Синтаксичний аналізатор сприймає вихід лексичного аналізатора і розбирає його у відповідності з граматикою вхідної мови, але в граматиці вхідної мови програмування ,як правило, не уточнюється які конструкції треба вважати лексемами і на практиці не існує правила того, які конструкції розпізнає лексичний аналізатор,а які синтаксичний - це визначає сам розробник компілятора, виходячи з синтаксису і семантики вхідної мови і технологічних особливостей програм.
Головну роль у функціонування синтаксичного аналізатора грає принцип побудови розпізнавача для контекстно-вільних мов. Оскільки перед синтаксичним аналізатором стоять 2 основні задачі: перевірити правильність конструкцій програми у виді вже виділених слів вхідної мови, перетворити її в вид зручний для семантичної обробки і генерації коду, то одним із способів синтаксичного розбору є дерево синтаксичного розбору.
Основою для побудови дерева є автомати з магазинною пам'яттю (МП-автомати). МП-автомат, на відміну від звичайного кінцевого автомата, має магазин (стек), в цей стек заносяться як правило термінальні і не термінальні символи граматики мови. Перехід автомата з магазинною пам'яттю з одного стану в інший залежить не тільки від вхідного символу,а ще і від одного або кількох символів з вершини стека.
Отже конфігурація МП-автомата визначається 3 параметрами:
1) станом автомату;
2) поточним символом вхідного ланцюжка;
3) вмістом стека;
При виконанні переходу автомату з однієї конфігурації в іншу із стека вилучаються верхні символи, які відповідають умові переходу і додається ланцюжок, який відповідає правилу переходу. Перший символ ланцюжка стає вершиною стеку. Допускаються переходи при яких вхідний символ пропускається і цей же символ буде вхідним символом при наступному переході (такі переходи називаються -переходами). Якщо при закінченні ланцюжка автомат знаходиться в одному з заданих кінцевих станів, а стек пустий - ланцюжок вважається прийнятим і після закінчення ланцюжка можуть бути зроблені -переходи, інакше ланцюжок не приймається. Автомат з МП називається недетермінованим, якщо при одній і тій же його конфігурації можливий більш ніж 1 перехід, якщо ж з будь-якої конфігурації автомата з МП при будь-якому вхідному символі можливий лише 1 перехід, то автомат з МП є детермінованим. Детерміновані автомати з МП задають клас детермінованих контекстно-вільних мов для яких існують однозначні контекстно-вільні граматики, саме цей клас мов лежить в основі синтаксичних конструкцій всіх мов програмування тому, що будь-яка синтаксична конструкція мови програмування повинна трактуватися компілятором однозначно.
1.4 Генерація об'єктного коду
Генерація об'єктного коду - це переведення компілятором внутрішнього представлення початкової програми в ланцюжок символів вихідної мови. Вихідною мовою компілятора, на відміну від транслятора, може бути або асемблер, або об'єктний код. Генерація об'єктного коду виконується після лексичного, синтаксичного і семантичного аналізу.
Компілятор виділяє закінчену синтаксичну конструкцію з тексту початкової програми, генерує для неї фрагмент результуючого коду і записує цей фрагмент в текст результуючої програми. Це повторюється для всіх синтаксичних конструкцій в програмі. Аналізуються такі синтаксичні конструкції як блоки операторів, описи процедур і функцій.
В лабораторній роботі результатом генерації коду є програма на мові асемблера. Взагалі є такі форми внутрішнього представлення програми:
1) структури зв'язних списків, що представляють синтаксичні дерева;
2) багатоадресний код з явно іменованими результатами (тетради);
3) багатоадресний код з неявно іменованими результатами (тріади);
4) зворотній постфікс ний польський запис операції;
5) асемблер ний код або машинні команди;
В кожному конкретному компіляторі розробник вибирає одну з даних форм. В даній лабораторній роботі використовується 3 форма.
Тріада - запис операцій в формі з 3 складових: операція і 2 операнди.
<операція> (<операнд 1><операнд 2>)
Особливістю тріад є те, що один або обидва операнди можуть бути посиланнями на іншу тріаду в тому випадку, якщо в якості операнда даної тріади виступає результат іншої тріади, тому тріади послідовно нумерують для зручності вказання посилань одних тріад на інші (в реалізації компілятора можуть бути не номери, а вказівники). Тоді при зміні нумерації і порядка слідування не треба змінювати посилання.
Приклад: A := B*C + D - B*10
1. *(B, C)
2. +(1^, D)
3. *(B, 10)
4. -(^2, ^3)
5. :=(A, ^4)
Тріади обчислюються послідовно одна за одною і результат обчислення зберігається в тимчасовій пам'яті.
Тріади дуже зручні для переведення їх кода в асемблер ний, але для тріад також потрібен алгоритм розподілу пам'яті для зберігання проміжних результатів обчислень, оскільки тимчасові змінні тріадами не використовуються (в цьому відмінність тріад від тетрад). Тріади не залежать від архітектури обчислювальної системи на яку орієнтована результуюча програма. Це машинно-незалежна форма внутрішнього представлення програми.
Переваги тріад:
1) вони є лінійною послідовністю операцій на відміну від синтаксичного дерева;
2) займають менше пам'яті ніж тетроди і краще оптимізуються ніж зворотній польський код;
3) явно відображають взаємодію операцій між собою;
4) результат обчислення тріад можна зберігати в регістрах процесора, що зручно для оптимізації;
вони наближені до двоадресних машинних команд, які найбільше поширені в наборах команд більшості комп'ютерів;
2. Таблиця передування
pr. |
end. |
; |
f |
( |
) |
else |
beg |
end |
rpt |
until |
a |
c |
:= |
or |
xor |
and |
< |
> |
= |
<> |
not |
- |
+ |
um |
! |
* |
/ |
||
pr. |
|
= |
< |
< |
|
|
|
< |
|
< |
|
< |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
end. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
> |
|
|
|
; |
|
> |
> |
< |
|
|
|
< |
> |
< |
|
< |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if |
|
|
|
|
= |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
( |
|
|
|
< |
= |
|
|
|
|
|
< |
< |
|
< |
< |
< |
< |
< |
< |
< |
< |
< |
< |
< |
|
< |
< |
||
) |
|
> |
> |
< |
|
> |
= |
< |
> |
< |
= |
< |
|
|
> |
> |
> |
> |
> |
> |
> |
|
> |
> |
|
|
> |
> |
|
else |
|
> |
> |
< |
|
|
> |
< |
> |
< |
|
< |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
beg. |
|
|
< |
< |
|
|
|
< |
= |
< |
|
< |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
end |
|
> |
> |
|
|
|
> |
|
> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
repit |
|
|
|
|
= |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
until |
|
> |
> |
< |
|
|
> |
< |
< |
< |
|
< |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
a |
|
> |
> |
|
|
> |
> |
|
> |
|
|
|
|
= |
> |
> |
> |
> |
> |
> |
> |
|
> |
> |
|
|
> |
> |
|
c |
|
> |
> |
|
|
> |
> |
|
> |
|
|
|
|
|
> |
> |
> |
> |
> |
> |
> |
|
> |
> |
|
|
> |
> |
|
:= |
|
> |
> |
|
< |
|
> |
|
> |
|
|
< |
< |
|
|
|
|
|
|
|
|
|
< |
< |
< |
|
< |
< |
|
or |
|
|
|
|
< |
> |
|
|
|
|
|
< |
< |
|
> |
> |
< |
< |
< |
< |
< |
< |
< |
< |
< |
|
< |
< |
|
xor |
|
|
|
|
< |
> |
|
|
|
|
|
< |
< |
|
> |
> |
< |
< |
< |
< |
< |
< |
< |
< |
< |
|
< |
< |
|
and |
|
|
|
|
< |
> |
|
|
|
|
|
< |
< |
|
> |
> |
> |
< |
< |
< |
< |
< |
< |
< |
< |
|
< |
< |
|
< |
|
|
|
|
< |
> |
|
|
|
|
|
< |
< |
|
> |
> |
> |
|
|
|
|
|
< |
< |
< |
|
< |
< |
|
> |
|
|
|
|
< |
> |
|
|
|
|
|
< |
< |
|
> |
> |
> |
|
|
|
|
|
< |
< |
< |
|
< |
< |
|
= |
|
|
|
|
< |
> |
|
|
|
|
|
< |
< |
|
> |
> |
> |
|
|
|
|
|
< |
< |
< |
|
< |
< |
|
<> |
|
|
|
|
< |
> |
|
|
|
|
|
< |
< |
|
> |
> |
> |
|
|
|
|
|
< |
< |
< |
|
< |
< |
|
not |
|
|
|
|
= |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- |
|
> |
> |
|
< |
> |
> |
|
> |
|
|
< |
< |
|
> |
> |
> |
> |
> |
> |
> |
|
> |
> |
< |
|
> |
> |
|
+ |
|
> |
> |
|
< |
> |
> |
|
> |
|
|
< |
< |
|
> |
> |
> |
> |
> |
> |
> |
|
> |
> |
< |
|
> |
> |
|
um |
|
> |
> |
|
< |
> |
> |
|
> |
|
|
< |
< |
|
> |
> |
> |
> |
> |
> |
> |
|
> |
> |
< |
|
> |
> |
|
! |
< |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
* |
|
> |
> |
|
< |
> |
> |
|
> |
|
|
< |
< |
|
> |
> |
> |
> |
> |
> |
> |
|
> |
> |
< |
|
> |
> |
|
/ |
|
> |
> |
|
< |
> |
> |
|
> |
|
|
< |
< |
|
> |
> |
> |
> |
> |
> |
> |
|
> |
> |
< |
|
> |
> |
3. Лістинг програм
1. FormLab
unit FormLab4;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls,
Forms, Dialogs, StdCtrls, ComCtrls, Grids, ExtCtrls,
LexElem, SyntSymb, Triads;
type
{ Типы возможных ошибок компилятора: файловая,
лексическая, синтаксическая, семантическая (триады)
или ошибок нет }
TErrType = (ERR_FILE, ERR_LEX, ERR_SYNT,
ERR_TRIAD, ERR_NO);
TCursovForm = class(TForm)
PageControl1: TPageControl;
SheetFile: TTabSheet;
SheetLexems: TTabSheet;
BtnExit: TButton;
GroupText: TGroupBox;
ListIdents: TMemo;
EditFile: TEdit;
BtnFile: TButton;
BtnLoad: TButton;
FileOpenDlg: TOpenDialog;
GridLex: TStringGrid;
SheetSynt: TTabSheet;
TreeSynt: TTreeView;
SheetTriad: TTabSheet;
GroupTriadAll: TGroupBox;
Splitter1: TSplitter;
GroupTriadSame: TGroupBox;
Splitter2: TSplitter;
GroupTriadConst: TGroupBox;
ListTriadAll: TMemo;
ListTriadConst: TMemo;
ListTriadSame: TMemo;
CheckDel_C: TCheckBox;
CheckDelSame: TCheckBox;
SheetAsm: TTabSheet;
ListAsm: TMemo;
CheckAsm: TCheckBox;
procedure BtnLoadClick(Sender: TObject);
procedure BtnFileClick(Sender: TObject);
procedure EditFileChange(Sender: TObject);
procedure BtnExitClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormClose(Sender: TObject;
var Action: TCloseAction);
private
{ Список лексем }
listLex: TLexList;
{ Синтаксический стек }
symbStack: TSymbStack;
{ Список триад }
listTriad: TTriadList;
{ Имена файлов: входного, результата и ошибок }
sInpFile,sOutFile,sErrFile: string;
{ Функция записи стартовых данных в файл ошибок }
procedure StartInfo(const sErrF: string);
{ Функция обработки командной строки }
procedure ProcessParams(
var flOptC,flOptSame,flOptAsm: Boolean);
{ Процедура инициализации таблицы
для отображения списка лексем }
procedure InitLexGrid;
{ Процедура отображения синтаксического дерева }
procedure MakeTree(nodeTree: TTreeNode;
symbSynt: TSymbol);
{ Процедура информации об ошибке }
procedure ErrInfo(const sErrF,sErr: string;
iPos,iLen: integer);
{ Функция запуска компилятора }
function CompRun(const sInF,sOutF,sErrF: string;
var symbRes: TSymbol;
flTrd,flDelC,flDelSame,flOptC,
flOptSame,flOptAsm: Boolean): TErrType;
public
{ Public declarations }
end;
var
CursovForm: TCursovForm;
implementation
{$R *.DFM}
uses FncTree, LexType, LexAuto, TrdType, TrdMake, TrdAsm,
TrdOpt;
procedure TCursovForm.InitLexGrid;
{ Процедура инициализации таблицы
для отображения списка лексем }
begin
with GridLex do
begin
RowCount := 2;
Cells[0,0] := '№ п/п';
Cells[1,0] := 'Лексема';
Cells[2,0] := 'Значение';
Cells[0,1] := '';
Cells[1,1] := '';
Cells[2,1] := '';
end;
end;
procedure TCursovForm.StartInfo(
const sErrF: string{имя файла ошибок});
{ Функция записи стартовых данных в файл ошибок }
var
i,iCnt: integer;{счетчик параметров и переменная цикла}
sTmp: string;{суммарная командная строка}
begin
{ Запоминаем имя файла ошибок }
sErrFile := sErrF;
{ Записываем в файл ошибок дату запуска компилятора }
ErrInfo(sErrFile,
Format('--- %s ---',[DateTimeToStr(Now)]),0,0);
{ Берем количество входных параметров }
iCnt := ParamCount;
{ Обнуляем суммарную командную строку }
sTmp := ParamStr(0);
{ Записываем в суммарную командную строку
все параметры последовательно }
for i:=1 to iCnt do
sTmp := sTmp +' '+ ParamStr(i);
{ Записываем в файл ошибок суммарную командную строку }
ErrInfo(sErrFile,sTmp,0,0);
end;
procedure TCursovForm.ProcessParams(
var flOptC,flOptSame,flOptAsm: Boolean{флаги});
{ Функция обработки командной строки }
var
i,iCnt,iLen: integer;
sTmp: string;
{ Список для записи ошибок параметров }
listErr: TStringList;
begin
{ Устанавливаем все флаги по умолчанию }
flOptC := True;
flOptSame := True;
flOptAsm := True;
{ Создаем список для записи ошибок параметров }
listErr := TStringList.Create;
try
{ Берем количество входных параметров }
iCnt := ParamCount;
{ Обрабатываем все параметры, начиная со второго }
for i:=2 to iCnt do
begin
{ Берем строку параметра }
sTmp := ParamStr(i);
{ Берем длину строки параметра }
iLen := Length(sTmp);
{ Если параметр слишком короткий или не начинается
со знака "-" - это неправильный параметр }
if (iLen < 3) or (sTmp[1] <> '-') then
{ Запоминаем ошибку в список }
listErr.Add(Format('Неверный параметр %d: "%s"!',
[i,sTmp]))
else
{ Иначе обрабатываем параметр в соответствии
с его типом (второй символ) }
case sTmp[2] of
{ Флаг оптимизации ассемблера }
'a','A': flOptAsm := (sTmp[3] = '1');
{ Флаг оптимизации методом свертки }
'c','C': flOptC := (sTmp[3] = '1');
{ Флаг оптимизации исключением лишних операций }
's','S': flOptSame := (sTmp[3] = '1');
{ Имя выходного файла }
'o','O': sOutFile := System.Copy(sTmp,3,iLen-2);
{ Имя файла ошибок }
'e','E': StartInfo(System.Copy(sTmp,3,iLen-2));
{ Параметр неизвестного типа }
else
{ Запоминаем ошибку в список }
listErr.Add(Format('Неверный параметр %d: "%s"!',
[i,sTmp]));
end{case};
end{for};
{ Ставим имена файлов по умолчанию,
если они не были указаны в параметрах }
if sOutFile = '' then
sOutFile := ChangeFileExt(sInpFile,'.asm');
if sErrFile = '' then
StartInfo(ChangeFileExt(sInpFile,'.err'));
{ Берем количество ошибок в параметрах }
iCnt := listErr.Count-1;
{ Запоминаем информацию обо всех ошибках }
for i:=0 to iCnt do ErrInfo(sErrFile,listErr[i],0,0)
{ Освобождаем память, занятую списком ошибок }
finally listErr.Free;
end{try};
end;
procedure TCursovForm.FormCreate(Sender: TObject);
var
flOptC,flOptSame,flOptAsm: Boolean;
symbRes: TSymbol;
iErr: TErrType;
begin
symbRes := nil;
sOutFile := '';
sErrFile := '';
{ В начале выполнения инициализируем список лесем,
таблицу идентификаторов, синтаксический стек
и список триад }
InitTreeVar;
listLex := TLexList.Create;
symbStack := TSymbStack.Create;
listTriad := TTriadList.Create;
{ Если указан параметр - не надо открывать окно,
надо сразу запускать компилятор
и обрабатывать входной файл }
if ParamCount > 0 then
begin
{ Берем имя входного файла из первого параметра }
sInpFile := ParamStr(1);
{ Обрабатываем все остальные параметры }
ProcessParams(flOptC,flOptSame,flOptAsm);
{ Запускаем компилятор }
iErr := CompRun(
sInpFile,sOutFile,sErrFile{входные файлы},
symbRes{ссылка на дерево разбора},
False{запоминать списки триад не надо},
flOptC{флаг удаления триад "C"},
flOptSame{флаг удаления триад "SAME"},
flOptC{флаг оптимизации по методу
свертки объектного кода },
flOptSame{флаг оптимизации исключ.
лишних операций},
flOptAsm{флаг оптимизации команд
ассемблера});
{ Если не было файловых ошибок,
то надо завершать работу }
if iErr <> ERR_FILE then Self.Close;
end;
end;
procedure TCursovForm.FormClose(Sender: TObject;
var Action: TCloseAction);
{ В конце выполнения очищаем список лесем,
таблицу идентификаторов, синтаксический стек
и список триад }
begin
listTriad.Free;
symbStack.Free;
listLex.Free;
ClearTreeVar;
Application.Terminate;
end;
procedure TCursovForm.EditFileChange(Sender: TObject);
begin
{ Можно читать файл, только когда его имя не пустое }
BtnLoad.Enabled := (EditFile.Text <> '');
end;
procedure TCursovForm.BtnFileClick(Sender: TObject);
begin
if FileOpenDlg.Execute then
{ Выбор имени файла с помощью стандартного диалога }
begin
EditFile.Text := FileOpenDlg.FileName;
BtnLoad.Enabled := (EditFile.Text <> '');
end;
end;
procedure TCursovForm.ErrInfo(const sErrF,sErr: string;
iPos,iLen: integer);
{ Процедура информации об ошибке }
var
{ Файл для записи информации об ошибке }
fileErr: TextFile;
begin
{ Если имя файла ошибок не пустое }
if sErrF <> '' then
try
{ Записываем информацию об ошибке в файл }
AssignFile(fileErr,sErrF);
if FileExists(sErrF) then Append(fileErr)
else Rewrite(fileErr);
writeln(fileErr,sErr);
{ и закрываем его }
CloseFile(fileErr);
except
{ Если была ошибка записи в файл, то
выдаем сообщение об этом }
MessageDlg(Format('Ошибка записи в файл "%s"!'#13#10
+ 'Ошибка компиляции: %s!',
[sErrF,sErr]),
mtError,[mbOk],0);
end
else
{ Если имя файла ошибок пустое,
выводим информацию на экран }
begin
{ Позиционируем список строк на место ошибки }
ListIdents.SelStart := iPos;
ListIdents.SelLength := iLen;
{ Выводим сообщение на экран }
MessageDlg(sErr,mtWarning,[mbOk],0);
{ Отображаем позицию ошибки в списке строк }
ListIdents.SetFocus;
end;
end;
{ Функция запуска компилятора }
function TCursovForm.CompRun(
const sInF,{имя входного файла}
sOutF,{имя результирующего файла}
sErrF{имя файла ошибок}:string;
var symbRes: TSymbol;{корень дерева разбора}
flTrd,{флаг записи триад в списки}
flDelC,{флаг удаления триад типа ""}
flDelSame,{флаг удаления триад типа ""}
flOptC,{флаг оптимизации методом свертки}
flOptSame,{флаг оптимизации методом
исключения лишних операций}
flOptAsm{флаг оптимизации ассемблерного кода}
: Boolean): TErrType;
var
i,iCnt,iErr: integer;
lexTmp: TLexem;
sVars,sAdd: string;
asmList: TStringList;
begin
{ Очищаем список лексем }
listLex.Clear;
{ Очищаем синтаксический стек }
symbStack.Clear;
{ Очищаем список триад }
listTriad.Clear;
try
{ Чтение файла в список строк }
ListIdents.Lines.LoadFromFile(sInF);
except
Result := ERR_FILE;
MessageDlg('Ошибка чтения файла!',mtError,[mbOk],0);
Exit;
end;
{ Анализ списка строк и заполнение списка лексем }
iErr := MakeLexList(ListIdents.Lines,listLex);
if iErr <> 0 then
{ Если анализ не успешный, выводим сообщение об ошибке }
begin
{ Берем позицию ошибочной лексемы из
фиктивной лексемы в начале списка }
ErrInfo(sErrF,
Format('Неверная лексема "%s" в строке %d!',
[listLex[0].LexInfoStr,iErr]),
listLex[0].PosAll,listLex[0].PosNum);
{ Результат - лексическая ошибка }
Result := ERR_LEX;
end
else
begin
{ Добавляем в конец списка лексем информационную
лексему "конец строки" }
with ListIdents do
listLex.Add(TLexem.CreateInfo('Конец строки',
Length(Text),Lines.Count-1,0));
{ Строим дерево синтаксического разбора и
получаем ссылку на его корень }
symbRes := BuildSyntList(listLex,symbStack);
{ Если эта ссылка содержит лексические данные,
значит была ошибка в месте, указанном лексемой }
if symbRes.SymbType = SYMB_LEX then
begin
{ Берем позицию ошибочной лексемы из
лексемы по ссылке }
ErrInfo(sErrF,
Format('Синтаксическая ошибка в строке %d поз. %d!',
[symbRes.Lexem.StrNum+1,symbRes.Lexem.PosNum]),
symbRes.Lexem.PosAll,0);
{ Освобождаем ссылку на лексему }
symbRes.Free;
symbRes := nil;
{ Результат - синтаксическая ошибка }
Result := ERR_SYNT;
end
else
{ Иначе ссылка указывает на корень
синтаксического дерева }
begin
{ Строим список триад по синтаксическому дереву }
lexTmp := MakeTriadList(symbRes,listTriad);
{ Если есть ссылка на лексему, значит была
семантическая ошибка }
if lexTmp <> nil then
begin
{ Берем позицию ошибочной лексемы по ссылке }
ErrInfo(sErrF,
Format('Семантическая ошибка в строке %d поз. %d!',
[lexTmp.StrNum+1,lexTmp.PosNum]),
lexTmp.PosAll,0);
{ Результат - семантическая ошибка }
Result := ERR_TRIAD;
end
else
{ Если есть ссылка пуста, значит триады построены }
begin
{ Результат - "ошибок нет" }
Result := ERR_NO;
{ Если указан флаг, сохраняем общий список триад }
if flTrd then
listTriad.WriteToList(ListTriadAll.Lines);
{ Если указан флаг, выполняем оптимизацию
методом свертки объектного кода }
if flOptC then
begin
OptimizeConst(listTriad);
{ Если указан флаг, удаляем триады типа "C" }
if flDelC then
DelTriadTypes(listTriad,TRD_CONST);
end;
{ Если указан флаг, сохраняем
список триад после оптимизации }
if flTrd then
listTriad.WriteToList(ListTriadConst.Lines);
{ Если указан флаг, выполняем оптимизацию
методом исключения лишних операций }
if flOptSame then
begin
OptimizeSame(listTriad);
{ Если указан флаг, удаляем триады типа "SAME" }
if flDelSame then
DelTriadTypes(listTriad,TRD_SAME);
end;
{ Если указан флаг, сохраняем
список триад после оптимизации }
if flTrd then
listTriad.WriteToList(ListTriadSame.Lines);
{ Распределяем регистры по списку триад }
iCnt := MakeRegisters(listTriad);
{ Создаем и записываем список ассемблерных команд }
asmList := TStringList.Create;
try
with asmList do
begin
{ Очищаем список ассемблерных команд }
Clear;
{ Пишем заголовок программы }
Add(Format('program %s;',[NAME_PROG]));
{ Запоминаем перечень всех идентификаторов }
sVars := IdentList(',',NAME_INPVAR,NAME_FUNCT);
if sVars <> '' then
begin
{ Если перечень идентификаторов не пустой,
записываем его с указанием типа данных }
Add('');
Add('var');
Add(Format(' %s: %s;',[sVars,NAME_TYPE]));
end;
Add('');
{ Пишем заголовок функции }
Add(Format('function %0:s(%1:s: %2:s): %2:s;'
+' stdcall;',
[NAME_FUNCT,NAME_INPVAR,NAME_TYPE]));
{ Если регистров для хранения промежуточных
результатов не хватило и нужны временные
переменные, то заполняем их список }
if iCnt > 0 then
begin
Add('var');
sVars := '';
for i:=0 to iCnt do
begin
sAdd := Format('%s%d',[TEMP_VARNAME,i]);
if sVars = '' then sVars := sAdd
else sVars := sVars +','+ sAdd;
end;
Add(Format(' %s: %s;',[sVars,NAME_TYPE]));
end;
{ В тело функции записываем созданный список
команд ассемблера }
Add('begin');
Add(' asm');
Add(#9'pushad'#9#9'{запоминаем регистры}');
MakeAsmCode(listTriad,asmList,flOptAsm);
Add(#9'popad'#9#9'{восстанавливаем регистры}');
Add(' end;');
Add('end;');
Add('');
{ Описываем одну входную переменную }
Add(Format('var %s: %s;',
[NAME_INPVAR,NAME_TYPE]));
Add('');
{ Заполняем тело главной программы }
Add('begin');
Add(Format(' readln(%s);',[NAME_INPVAR]));
Add(Format(' writeln(%s(%s));',
[NAME_FUNCT,NAME_INPVAR]));
Add(' readln;');
Add('end.');
end{with};
{ Если установлен флаг, записываем список команд
в список для отображения на экране }
if flTrd then
ListAsm.Lines.AddStrings(asmList);
{ Если имя результирующего файла не пустое,
записываем туда список всех команд }
if sOutF <> '' then
try
asmList.SaveToFile(sOutF);
except Result := ERR_FILE;
end;
finally asmList.Free;
end{try};
end;
end;
end;
end;
procedure TCursovForm.BtnLoadClick(Sender: TObject);
{ Процедура чтения и анализа файла }
var
i,iCnt: integer;
iRes: TErrType;
symbRes: TSymbol;
nodeTree: TTreeNode;
begin
symbRes := nil;
{ Очищаем таблицу отображения списка лексем
и синтаксическое дерево }
InitLexGrid;
TreeSynt.Items.Clear;
ListAsm.Clear;
{ Вызываем функцию компиляции }
iRes := CompRun(
EditFile.Text,'','',{задан только входной файл}
symbRes{указатель на дерево разбора},
True{Списки триад нужно запоминать},
CheckDel_C.Checked
{флаг удаления триад "C"},
CheckDelSame.Checked
{флаг удаления триад "SAME"},
True{флаг оптимизации по методу
свертки объектного кода },
True{флаг оптимизации исключ.
лишних операций},
CheckAsm.Checked{флаг оптимизации
команд ассемблера});
if iRes > ERR_LEX then
{ Если не было лексической ошибки,
заполняем список лепксем }
begin
{ Цикл по всем прочитанным лексемам }
GridLex.RowCount := listLex.Count+1;
iCnt := listLex.Count-1;
for i:=0 to iCnt do
begin
{ Первая колонка - номер }
GridLex.Cells[0,i+1] := IntToStr(i+1);
{ Вторая колонка - тип лексемы }
GridLex.Cells[1,i+1] :=
LexTypeName(listLex[i].LexType);
{ Третья колонка - значение лексемы }
GridLex.Cells[2,i+1] := listLex[i].LexInfoStr;
end;
end;
if (iRes > ERR_SYNT) and (symbRes <> nil) then
{ Если не было синтаксической ошибки,
заполняем дерево синтаксического разбора }
begin
{ Записываем данные в корень дерева }
nodeTree := TreeSynt.Items.Add(nil,symbRes.SymbolStr);
{ Строим остальные элементы дерева от его корня }
MakeTree(nodeTree,symbRes);
{ Раскрываем всё дерево }
nodeTree.Expand(True);
{ Позиционируем указатель на корневой элемент }
TreeSynt.Selected := nodeTree;
end;
if iRes > ERR_TRIAD then
{ Если не было семантической ошибки,
то компиляция успешно запвершена }
begin
MessageDlg('Компиляция успешно выполнена!',
mtInformation,[mbOk],0);
PageControl1.ActivePageIndex := 4;
end;
end;
procedure TCursovForm.MakeTree(
{ Процедура отображения синтаксического дерева }
nodeTree: TTreeNode;
{ссылка на корневой элемент отображаемой
части дерева на экране}
symbSynt: TSymbol
{ссылка на синтаксический символ, связанный
с корневым элементом этой части дерева});
var
i,iCnt: integer;
nodeTmp: TTreeNode;
begin
{ Берем количество дочерних вершин для текущей }
iCnt := symbSynt.Count-1;
{ Цикл по всем дочерним вершинам }
for i:=0 to iCnt do
begin
{ Добавляем к дереву на экране вершину и
запоминаем ссылку на нее }
nodeTmp := TreeSynt.Items.AddChild(nodeTree,
symbSynt[i].SymbolStr);
{ Если эта вершина связана с нетерминальным символом,
рекурсивно вызываем процедуру построения дерева
для нее }
if symbSynt[i].SymbType = SYMB_SYNT then
MakeTree(nodeTmp,symbSynt[i]);
end;
end;
procedure TCursovForm.BtnExitClick(Sender: TObject);
{ Завершение работы с программой }
begin
Self.Close;
end;
end.
2. FncTree
unit FncTree;
interface
{ Модуль, обеспечивающий работу с комбинированной таблицей
идентификаторов, построенной на основе хэш-функции и
бинарного дерева }
uses TblElem;
{ Функция начальной инициализации хэш-таблицы }
procedure InitTreeVar;
{ Функция освобождения памяти хэш-таблицы }
procedure ClearTreeVar;
{ Функция удаления дополнительной информации в таблице }
procedure ClearTreeInfo;
{ Добавление элемента в таблицу идентификаторов }
function AddTreeVar(const sName: string): TVarInfo;
{ Поиск элемента в таблице идентификаторов }
function GetTreeVar(const sName: string): TVarInfo;
{ Функция, возвращающая количество операций сравнения }
function GetTreeCount: integer;
{ Функция записи всех имен идентификаторов в одну строку }
function IdentList(const sLim,sInp,sOut: string): string;
implementation
const
{ Минимальный и максимальный элементы хэш-таблицы
(охватывают весь диапазон значений хэш-функции) }
HASH_MIN = Ord('0')+Ord('0');
HASH_MAX = Ord('z')+Ord('z');
var
HashArray : array[HASH_MIN..HASH_MAX] of TVarInfo;
{ Массив для хэш-таблицы }
iCmpCount : integer;
{ Счетчик количества сравнений }
function GetTreeCount: integer;
begin
Result := iCmpCount;
end;
function IdentList(const sLim,sInp,sOut: string): string;
{ Функция записи всех имен идентификаторов в одну строку }
var
i: integer; { счетчик идентификаторов }
sAdd: string; { стока для временного хранения данных }
begin
{ Первоначально строка пуста }
Result := '';
{ Цикл по всем идентификаторам в таблице }
for i:=HASH_MIN to HASH_MAX do
begin
{ Если ячейка таблицы пустая, то добавлять ничего
не нужно, }
if HashArray[i] = nil then sAdd := ''
{ иначе вычисляем добавочную часть строки }
else sAdd := HashArray[i].GetElList(sLim,sInp,sOut);
{ Если добавочная часть строки не пуста,
то добавляем ее в общую строку через разделитель }
if sAdd <> '' then
begin
if Result <> '' then Result := Result + sLim + sAdd
else Result := sAdd;
end;
end{for};
end;
function VarHash(const sName: string): longint;
{ Хэш функция - сумма кодов первого и среднего
символов строки }
begin
Result := (Ord(sName[1])
+ Ord(sName[(Length(sName)+1) div 2])
- HASH_MIN) mod (HASH_MAX-HASH_MIN+1)+HASH_MIN;
if Result < HASH_MIN then Result := HASH_MIN;
end;
procedure InitTreeVar;
{ Начальная инициализация хэш-таблицы -
все элементы пустые }
var i : integer;
begin
for i:=HASH_MIN to HASH_MAX do HashArray[i] := nil;
end;
procedure ClearTreeVar;
{ Освобождение памяти для всех элементов хэш-таблицы }
var i : integer;
begin
for i:=HASH_MIN to HASH_MAX do
begin
HashArray[i].Free;
HashArray[i] := nil;
end;
end;
procedure ClearTreeInfo;
{ Удаление дополнительной информации для всех
элементов хэш-таблицы }
var i : integer;
begin
for i:=HASH_MIN to HASH_MAX do
if HashArray[i] <> nil then HashArray[i].ClearAllInfo;
end;
function AddTreeVar(const sName: string): TVarInfo;
{ Добавление элемента в хэш-таблицу и дерево }
var iHash: integer;
begin
{ Обнуляем счетчик количества сравнений }
iCmpCount := 0;
{ Вычисляем хэш-адрес с помощью хэш-функции }
iHash := VarHash(Upper(sName));
if HashArray[iHash] <> nil then
Result := HashArray[iHash].AddElCnt(sName,iCmpCount)
else
begin
Result := TVarInfo.Create(sName);
HashArray[iHash] := Result;
end;
end;
function GetTreeVar(const sName: string): TVarInfo;
var iHash: integer;
begin
iCmpCount := 0;
iHash := VarHash(Upper(sName));
if HashArray[iHash] = nil then Result := nil
else
Result := HashArray[iHash].FindElCnt(sName,iCmpCount)
end;
initialization
{ Вызов начальной инициализации таблицы
при загрузке модуля }
InitTreeVar;
finalization
{ Вызов освобождения памяти таблицы при выгрузке модуля }
ClearTreeVar;
end.
3. TblElem
unit TblElem;
interface
{ Модуль, описывающий структуру данных элементов
таблицы идентификаторов }
type
{ Класс для описания некоторых данных,
связанных с элементом таблицы }
TAddVarInfo = class(TObject)
public
procedure SetInfo(iIdx: integer; iInfo: longint);
virtual; abstract;
function GetInfo(iIdx: integer): longint;
virtual; abstract;
property Info[iIdx: integer]: longint
read GetInfo write SetInfo; default;
end;
{ Класс для описания элемента хэш-таблицы }
TVarInfo = class(TObject)
protected
{ Имя элемента }
sName: string;
{ Дополнительная информация (для будущего использования)}
pInfo: TAddVarInfo;
{ Ссылки на меньший и больший элементы
для организации бинарного дерева }
minEl,maxEl: TVarInfo;
public
{ Конструктор создания элемента хэш-таблицы }
constructor Create(const sN: string);
{ Деструктор для освобождения памяти, занятой элементом }
destructor Destroy; override;
{ Функция заполнения дополнительной информации элемента }
procedure SetInfo(pI: TAddVarInfo);
{ Функции для удаления дополнительной информации }
procedure ClearInfo;
procedure ClearAllInfo;
{ Свойство "Имя элемента" }
property VarName: string read sName;
{ Свойство "Дополнительная информация"
(для будущего использования) }
property Info: TAddVarInfo read pInfo write SetInfo;
{ Функции для добавления элемента в бинарное дерево }
function AddElCnt(const sAdd: string;
var iCnt: integer): TVarInfo;
function AddElem(const sAdd: string): TVarInfo;
{ Функции для поиска элемента в бинарном дереве }
function FindElCnt(const sN: string;
var iCnt: integer): TVarInfo;
function FindElem(const sN: string): TVarInfo;
{ Функция записи всех имен идентификаторов
в одну строку }
function GetElList(const sLim,sInp,sOut: string): string;
end;
function Upper(const x:string): string;
implementation
uses SysUtils;
{ Условная компиляция: если определено имя REGNAME,
то имена переменных считаются регистрозависимыми,
иначе - регистронезависимыми }
{$IFDEF REGNAME}
function Upper(const x:string): string;
begin Result := UpperCase(x); end;
{$ELSE}
function Upper(const x:string): string;
begin Result := x; end;
{$ENDIF}
constructor TVarInfo.Create(const sN: string);
{ Конструктор создания элемента хэш-таблицы }
begin
{ Вызываем конструктор базового класса TObject }
inherited Create;
{ Запоминаем имя элемента и обнуляем все ссылки }
sName := sN;
pInfo := nil;
minEl := nil;
maxEl := nil;
end;
destructor TVarInfo.Destroy;
{ Деструктор для освобождения памяти, занятой элементом }
begin
{ Освобождаем память по каждой ссылке
(если она не нулевая), при этом в дереве рекурсивно
будет освобождена память для всех элементов }
ClearAllInfo;
minEl.Free;
maxEl.Free;
{ Вызываем деструктор базового класса TObject }
inherited Destroy;
end;
function TVarInfo.GetElList(const sLim{разделитель списка},
sInp,sOut{имена, не включаемые в строку}: string): string;
{ Функция записи всех имен идентификаторов в одну строку }
var sAdd: string;
begin
{ Первоначально строка пуста }
Result := '';
{ Если элемент таблицы не совпадает с одним
из невключаемых имен, то его нужно включить в строку }
if (Upper(sName) <> Upper(sInp))
and (Upper(sName) <> Upper(sOut)) then Result := sName;
{ Если есть левая ветвь дерева }
if minEl <> nil then
begin
{ Вычисляем строку для этой ветви }
sAdd := minEl.GetElList(sLim,sInp,sOut);
{ Если она не пустая, добавляем ее через разделитель }
if sAdd <> '' then
begin
if Result <> '' then Result := Result + sLim + sAdd
else Result := sAdd;
end;
end;
{ Если есть правая ветвь дерева }
if maxEl <> nil then
begin
{ Вычисляем строку для этой ветви }
sAdd := maxEl.GetElList(sLim,sInp,sOut);
{ Если она не пустая, добавляем ее через разделитель }
if sAdd <> '' then
begin
if Result <> '' then Result := Result + sLim + sAdd
else Result := sAdd;
end;
end;
end;
procedure TVarInfo.SetInfo(pI: TAddVarInfo);
{ Функция заполнения дополнительной информации элемента }
begin
pInfo := pI;
end;
procedure TVarInfo.ClearInfo;
{ Функция удаления дополнительной информации элемента }
begin
pInfo.Free;
pInfo := nil;
end;
procedure TVarInfo.ClearAllInfo;
{ Функция удаления дополнительной информации элемента
и его связок }
begin
if minEl <> nil then minEl.ClearAllInfo;
if maxEl <> nil then maxEl.ClearAllInfo;
pInfo.Free;
pInfo := nil;
end;
function TVarInfo.AddElCnt(const sAdd: string;
var iCnt: integer): TVarInfo;
{ Функция добавления элемента в бинарное дерево
с учетом счетчика сравнений }
var i: integer;
begin
{ Увеличиваем счетчик сравнений }
Inc(iCnt);
{ Сравниваем имена нового и текущего элемента
(одной функцией!) }
i := StrComp(PChar(Upper(sAdd)),PChar(Upper(sName)));
if i < 0 then
{ Если новый элемент меньше, смотрим ссылку
на меньший элемент }
begin
{ Если ссылка не пустая, рекурсивно вызываем функцию
добавления элемента }
if minEl <> nil then
Result := minEl.AddElCnt(sAdd,iCnt)
else
{ Если ссылка пустая, создаем новый элемент
и запоминаем ссылку на него }
begin
Result := TVarInfo.Create(sAdd);
minEl := Result;
end;
end
else
{ Если новый элемент больше, смотрим ссылку
на больший элемент }
if i > 0 then
begin
{ Если ссылка не пустая, рекурсивно вызываем функцию
добавления элемента }
if maxEl <> nil then
Result := maxEl.AddElCnt(sAdd,iCnt)
else
{ Если ссылка пустая, создаем новый элемент
и запоминаем ссылку на него }
begin
Result := TVarInfo.Create(sAdd);
maxEl := Result;
end;
end
{ Если имена совпадают, то такой элемент уже есть
в дереве - это текущий элемент }
else Result := Self;
end;
function TVarInfo.AddElem(const sAdd: string): TVarInfo;
{ Функция добавления элемента в бинарное дерево
без учета счетчика сравнений }
var iCnt: integer;
begin
Result := AddElCnt(sAdd,iCnt);
end;
function TVarInfo.FindElCnt(const sN: string;
var iCnt: integer): TVarInfo;
{ Функция поиска элемента в бинарном дереве
с учетом счетчика сравнений }
var i: integer;
begin
{ Увеличиваем счетчик сравнений }
Inc(iCnt);
{ Сравниваем имена искомого и текущего элемента
(одной функцией!) }
i := StrComp(PChar(Upper(sN)),PChar(Upper(sName)));
if i < 0 then
{ Если искомый элемент меньше, смотрим ссылку
на меньший элемент }
begin
{ Если ссылка не пустая, рекурсивно вызываем для нее
функцию поиска элемента, иначе элемент не найден }
if minEl <> nil then Result := minEl.FindElCnt(sN,iCnt)
else Result := nil;
end
else
if i > 0 then
{ Если искомый элемент больше, смотрим ссылку
на больший элемент }
begin
{ Если ссылка не пустая, рекурсивно вызываем для нее
функцию поиска элемента, иначе элемент не найден }
if maxEl <> nil then Result := maxEl.FindElCnt(sN,iCnt)
else Result := nil;
end
{ Если имена совпадают, то искомый элемент
найден - это текущий элемент }
else Result := Self;
end;
function TVarInfo.FindElem(const sN: string): TVarInfo;
{ Функция поиска элемента в бинарном дереве
без учета счетчика сравнений }
var iCnt: integer;
begin
Result := FindElCnt(sN,iCnt);
end;
end.
4. LexElem
unit LexElem;
interface
{ Модуль, описывающий структуру элементов таблицы лексем }
uses Classes, TblElem, LexType;
type
{ Структура для информации о лексемах }
TLexInfo = record
case LexType: TLexType of
LEX_VAR: (VarInfo: TVarInfo);
LEX_CONST: (ConstVal: integer);
LEX_START: (szInfo: PChar);
end;
{ Структура для описания лексемы }
TLexem = class(TObject)
protected
{ Информация о лексеме }
LexInfo: TLexInfo;
{ Позиция лексемы в исходном тексте программы }
iStr,iPos,iAllP: integer;
public
{ Конструкторы для создания лексем разных типов}
Подобные документы
Огляд існуючих методів розробки компіляторів, детальний опис мови. Характеристика та специфіка процесу розробки програми компілятора на рівні блок-схем і тексту програми. Подання тексту компілятора, а також результатів тестування розробленої програми.
курсовая работа [510,2 K], добавлен 03.06.2011Методика розробки компілятору з вхідної мови програмування Pascal, оболонка, якого розроблена в середовищі програмування Borland C під операційну систему Windows. Блок-схема програми. Розробка оптимізатора та генератора коду. Тестування компілятора.
курсовая работа [218,6 K], добавлен 04.06.2011Поняття компілятора та теоретичні основи його роботи. Введення коду програми завантаженням текстового файлу. Опрацювання тексту лексичним та синтаксичним аналізаторами. Генерація та оптимізанія об'єктного коду. Побудова графічного інтерфейсу програми.
курсовая работа [586,6 K], добавлен 22.01.2014Мови програмування, на яких написана програма побудови замкнутих багатокутників. Функціональні обмеження на застосування. Методи та елементи, що використовуються. Структура програми з описом функцій складових частин. Зв'язок програми з іншими програмами.
курсовая работа [76,6 K], добавлен 01.04.2016Створення додатку який дозволяє будувати діаграми динаміки обсягів промислового виробництва засобами інтегрованого середовища Borland Builder C++ 6.0 на мові програмування високого рівня С++. Опис структури інтерфейсу та складових частин програми.
курсовая работа [2,0 M], добавлен 15.01.2014Розробка програми для моделювання роботи алгоритму Дейкстри мовою C# з використанням об’єктно-орієнтованих принципів програмування. Алгоритм побудови робочого поля. Програмування графічного інтерфейсу користувача. Тестування програмного забезпечення.
курсовая работа [991,4 K], добавлен 06.08.2013Середовище розробки програм Borland Delphi, робота компонентів. Створення нових компонентів та використання компонентів Delphi для роботи з базами даних. Системи керування базами даних InterBase та Firebird. Компоненти Delphi для роботи з СКБД FireBird.
реферат [71,4 K], добавлен 12.04.2010Проектування гнучкої спеціалізованої системи генерації тестових завдань, яка відбувається на основі параметричної моделі з використанням зовнішніх компіляторів мов програмування Pascal і Borland C++. Середовище Delphi, як засіб розробки даної програми.
дипломная работа [2,4 M], добавлен 26.10.2012Основні відомості про історію розвитку мови Object Pascal, середовища Delphi, їх основні технології та застосування для роботи з файлами. Опис основних особливостей мови, основних елементів програмної мови. Принципи об'єктно-орієнтованого програмування.
курсовая работа [471,5 K], добавлен 12.04.2010Розгляд матеріалу з розрахунку рецептур. Аналоги програм та сайтів по розрахунку рецептур, створення алгоритму побудови програми. Оптимізація калькулятору з розрахунку рецептур. Проектування алгоритму та програмного забезпечення для його реалізації.
курсовая работа [52,0 M], добавлен 28.03.2023