Створення компілятора

Вивчення складових частин, основних принципів побудови і функціонування компіляторів. Поняття хешування, сутність алгоритму роботи лексичного аналізатора. Практичне освоєння методів побудови простих компіляторів для заданої вхідної мови - 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

Работы в архивах красиво оформлены согласно требованиям ВУЗов и содержат рисунки, диаграммы, формулы и т.д.
PPT, PPTX и PDF-файлы представлены только в архивах.
Рекомендуем скачать работу.