1.2.3. Семантичний аналіз
У процесі семантичного аналізу перевіряється наявність семантичних помилок у вихідній програмі і накопичується інформація про типи для наступної стадії – генераціі коду. При семантичному аналізі використовуються ієрархічні структури, отримані під час синтаксичного аналізу для ідентифікації операторів і операндів виразів та інструкцій.
Важливим аспектом семантичного аналізу є перевірка типів, коли компілятор перевіряє, що кожен оператор має операнди допустимого специфікаціями мови типу. Наприклад, визначення багатьох мов програмування вимагає, щоб при використанні дійсного числа в якості індексу масиву генерувалося повідомлення про помилку. У той же час специфікація мови може дозволити певне насильницьке перетворення типів, наприклад, коли бінарний арифметичний оператор застосовується до операндів цілого і дійсного типів. У цьому випадку комплятору може знадобитися перетворення цілого числа в дійсне.
Приклад 1.1
Бітове представлення цілого числа в комп'ютері, взагалі кажучи, відрізняється від бітового подання дійсного числа, навіть якщо ці числа мають одне і те ж значення. Припустимо, що всі ідентифікатори на рис. 1.5 оголошено як такі, що мають дійсний тип, а 60 трактується як ціле число. При перевірці типів на рис. 1.5 буде виявлено, що оператор * застосовується до дійсного числа гаte і цілого 60. Зазвичай при цьому здійснюється перетворення цілого числа в дійсне; на рис. 1.6 для цього створюється додатковий вузол для оператора intoteal, який неявно перетворює ціле число в дійсне. Однак, оскільки операнд оператора intoteal являє собою константу, компілятор може замість цього сам замінити цілу константу на еквівалентну дійсну.
Рис. 1.6. Семантичний аналіз додає перетворення з цілого числа в дійсне
1.3 Фази компілятора
Концептуально компілятор працює пофазно, причому в процесі кожної фази здійснюється перетворення початкової програми з одного представлення в інше. Типове розгалуження компілятора на фази показано на рис. 1.7. На практиці деякі фази можуть бути згруповані разом, і проміжні представлення програми всередині таких груп можуть явно не будуватися.
Перші три фази, що формують анализуючу частину компілятора, були розглянуті в попередньому розділі. Управління таблицею символів й обробка помилок показані у взаємодії з шістьма фазами: лексичним аналізом, синтаксичним аналізом, семантичним аналізом, генерацією проміжного коду, оптимізацією коду і генерацією коду. Неформально диспетчер таблиці символів і обробник помилок також можуть вважатися "фазами" компілятора.
1.3.1 Управління таблицею символів
Однією з важливих функцій компілятора є запис використовуваних у вихідній програмі ідентифікаторів і збір інформації про різні атрибути кожного ідентифікатора. Ці атрибути надають відомості про пам’ять, відведену ідентифікатору, його тип, області видимості (де в програмі він може застосовуватися). При викокористуванні імен процедур атрибути говорять про кількість і тип їх аргументів, метод передачі кожного аргументу (наприклад, за посиланням) і тип значення, що повертається, якщо таке є.
Таблиця символів являє собою структуру даних, що містить записи про кожен ідентифікатор з полями для його атрибутів. Дана структура дозволяє швидко знайти інформацію, наприклад, про будь-який ідентифікатор і внести необхідні зміни.
Якщо лексичним аналізатором у вихідній програмі виявлений ідентифікатор, він записується в таблицю символів. Проте атрибути ідентифікатора не завжди можуть бути визначені в процесі лексичного аналізу. Наприклад, в оголошенні змінних мовою Раsсаl
var position, initial, rate : геаl;
коли лексичний аналізатор знаходить ідентифікатори position, initial і rate, їх тип rеаl ще невідомий.У процесі інших фаз інформація про ідентифікатори вноситься в таблицю символів і використовується різними способами. Наприклад, при семантичному аналізі і генерації проміжного коду необхідно знати типи ідентифікаторів, щоб гарантувати їх коректне використання у вихідній програмі і згенерувати правильні операції щодо роботи з ними. Зазвичай генератор коду звертається до таблиці символів і використовує детальну інформацію про пам'ять, призначену ідентифікаторам.
1.3.2 Виявлення помилок та повідомлення про них
У кожній фазі компіляції можуть зустрітися помилки. Однак після їх виявлення необхідні певні дії, щоб продовжити компіляцію і виявити інші помилки у вхідній програмі. Компілятор, який зупиняється при виявленні першої ж помилки, не настільки корисний в роботі, яким міг би бути.
У процесі синтаксичного і семантичного аналізу зазвичай обробляється більша частина помилок, що виявляються компілятором. При лексичному аналізі виявлються помилки, при яких символи з вхідного потоку не формують жоден з токенів мови. Помилки, при яких потік токенів порушує структурні правила (синтаксис) мови, визначаються у фазі синтаксичного аналізу. У процесі семантічного аналізу компілятор намагається виявити конструкції, коректні з точки зору синтаксису, але в той же час вони не мають сенсу з точки зору виконуваних операцій, наприклад, спроба складання двох ідентифікаторів, один з яких - ім'я масиву, а другий - ім'я процедури. Обробка помилок в кожній фазі компіляції буде розглянута детальніше у розділах, присвячених кожної з фаз окремо. На рис. 1.8 продемонстровано наведені вище фази на прикладі трансляції оператора присвоєння змінній значення арифметичного виразу, що наводився вище. При цьому мається на увазі, що типи змінних position, initial та rate у декларативній частині було визначено як real.
