Компилаторът (англ. compiler, от compile – съчетавам, съставям) е компютърна програма, която превежда (компилира) даден компютърен изходен код в семантично отговарящ код на език от (обикновено) по-ниско ниво. Целевият език може да бъде машинен език или асемблерен език за конкретен процесор или процесорна фамилия, както и междинен език за конкретна виртуална машина (например байткод за виртуална машина на Java) или друг език от високо ниво. Когато се компилира до машинен език, крайният продукт е изпълнима програма или обектен код.

Стъпки на превеждане

редактиране

Има две основни фази при създаването на кода:

  • Аналитична, при която се изследва изходният код, като резултатът е синтактична дървовидна структура съдържаща (спомагателни) атрибути.
  • Синтезираща (съчетаваща), където се създава крайният код въз основа на данните получени от предишната фаза.

Аналитичната фаза

редактиране

Нарича се също така предно стъпало (frontend).

Лексически анализ

редактиране

Изходният код се разгражда на отделни лексикални единици – токени (tokens), напр. ключови думи, идентификатори, числа и оператори. С това се занимава програма, наречена лексически анализатор или скенер.

Лексическият анализатор понякога използва решетъчник (screener), който „пресява“ програмния код от белите знаци (whitespace) и от коментарите.

Синтактичен анализ

редактиране

При тази стъпка се проверява дали използваните градивни единици са формално допустими, т.е. дали отговарят на зададената граматика за програмния език. За целта програмният код се превръща в синтактична дървовидна структура.

Семантичен анализ

редактиране

Следващата стъпка е да се провери дали зададените програмни единици са логически свързани помежду си. Например:

  • дали една променлива е обявена за използване (т.е. дали е заделена оперативна памет за нея), преди да бъде използвана в програмата,
  • при прехвърляне на стойността на една променлива в друга, дали типовете на данните им са съвместими.

Тези проверки се осъществяват посредством атрибутивна граматика (attribute grammar).

Синтезираща фаза

редактиране

Нарича се също така задно стъпало (backend), където създадената синтактична дървовидна структура се превежда в крайния код.

Създаване на междинен код

редактиране

При тази стъпка се създава междинен код, който по правило е близък до структурата на целевия език. Чрез него се осъществява и подобрение на бързината на изпълнение. Това стъпало се използва и за изходна точка при създаване на код за различни платформи.

Оптимизация

редактиране

Това е най-сложната част от превеждането на програмите. Оптимизацията може да бъде в различни насоки, в зависимост от желаната цел:

  • увеличаване на бързината на изпълнение
  • намаляване на използваната оперативна памет
  • намаляване на разхода на електроенергия
  • намаляване на размера на кода

Окончателно създаване на код

редактиране

При таза стъпка окончателно се превежда синтактичната дървовидна структура на програмата в целевия език. Ако последният е машинен език, създаденият код може директно да бъде изпълнен или се създава така нареченият обектен файл, който чрез свързване (linking) с други обектни файлове и библиотеки води до създаване на изпълним код.

Свързването се нарича статично ако необходимите програмни функции от библиотеки или програми се „копират“ в машинния код директно. Свързването се нарича динамично, ако програмата се компилира с използване на споделени библиотеки, които се зареждат в оперативната памет, само когато са нужни. Самият код съдържа извикване на функциите от тези библиотеки. Тези библиотеки използват относителни адреси в паметта и целта на програмата зареждач на операционната система е да нагоди адресите според адресното пространство на стартираната програма. Такива библиотеки се казва, че са компилирани с позиционно независим код. Същестува и възможността за компилиране с позиционно зависим код, който се използва в определени случаи тъй като пропускането на процеса на релокация спестява изчислителни ресурси и води до подобрение на скоростта на изпълнение. Позиционно зависим код се използва и при вградените ОС като Windows CE, където програмите се зареждат винаги на строго определен адрес.

Видове компилатори

редактиране
  • Местен (native) компилатор
Резултатът от превеждането е код, изпълним на платформата, върху която върви самият компилатор.
  • Смесен (cross) компилатор
Резултатът е изпълним код, предназначен за платформа различна от тази на която е извършена компилацията. Използва се например при създаване на код за вградени системи (embedded systems), където няма възможност, или е по-трудно, да се създават програми.
  • Еднопреходен компилатор (single-pass compiler), също ограничен компилатор (narrow compiler)
Този компилатор превежда програмата последователно и на един път (single-pass) за всеки компилиран блок. Казано по друг начин, компилаторът не се връща обратно на вече преведени части. Много програмни езици са замислени така, че да бъдат превеждани еднопреходно (напр. Паскал).
  • Многопреходен компилатор (multi-pass compiler), също разширен компилатор (wide compiler)
Този компилатор превежда програмата на няколко стъпки и намира приложение най-вече за:
  • прекъсване на предходни заявявания на променливите (forward declaration, forward reference)
  • провеждане на оптимизациите (свързани с изразходване на много ресурси) върху пълната синтактична дървовидна структура

Особени видове компилатори

редактиране
  • Транскомпилатор (също транспилатор) е особен компилатор, който превежда програмен код от един програмен език на друг програмен език, например от Паскал на C.
  • Компилатор-Компилатор (compilergenerator) или помощни програми, създаващи автоматично компилаторни части или цели компилатори. Такива програми са напр. JavaCC, Yacc, Bison
  • На-момента-компилатори (англ. Just-In-Time compiler/JIT compiler) превеждащи кода преди самото му изпълняване.