Принципы построения трансляторов

Трансляторы, компоновщики и загрузчики.

Лекция 14

В воображении студентов, впервые приступивших к изучению курса программирования, часто возникает ореол таинственности вокруг компилятора программ. ЭВМ считывает текст программы, написанный на каком-либо языке, и эта программа неким магическим образом выполняется и выдает результаты. Чтобы рассеять заблуждения, достаточно понять, что ЭВМ способна выполнять только очень простые действия, предписанные машинными командами, а назначение компилятора состоит в преобразовании исходного текста программы в формат машинных команд. Компилятор является программой, которая способна воспринимать строку символов определенного вида (т.е. текст программы на исходном языке) и выдавать другую строку символов (программу в машинных кодах).

Например, предложение вида

X:=Y+Z;

компилятор может преобразовать в следующую последовательность машинных кодов:

A1 52 00 03 06 54 00 A3 50 00

mov ax,[0052h] add ax,[0054h] mov [0050h],ax

где [0050h], [0052h], [0054h] – адреса переменных X,Y,Z

Основой любого естественного или искусственного языка является алфавит – набор допустимых в языке элементарных знаков (букв, цифр и служебных знаков). Знаки могут объединяться в слова (лексемы) – элементарные конструкции языка, рассматриваемые в тексте программы как неделимые символы, имеющие определенный смысл. Словом может быть и одиночный символ, например запятая. В языке Паскаль словами являются идентификаторы, ключевые слова, числовые константы, знаки арифметических и логических операций, скобки, запятые и др. символы. Словарный состав языка вместе с описанием способов их представления составляют лексику языка.

Слова объединяются в предложения. Простейшим предложением является оператор (присваивания, сложения, умножения и т.д). Предложения из одного и более простых слов строятся по правилам синтаксиса. Синтаксис – это описание конструкций правильных предложений. Например, оператор присваивания имеет синтаксис

<ИМЯ>:= <ВЫРАЖЕНИЕ>;

Описание смысла предложений, то есть значений слов и их внутренних связей, составляет семантику языка. Правильные с точки зрения синтаксиса предложения могут содержать семантические ошибки. Например, если MyStr – строка символов длиной 5 байт,

var MyStr: string[5];

то синтаксически правильное выражение

MyStr:= ‘ABCDEF’;

является семантически неверным, так как длина выражения превышает длину переменной.

Семантическими ошибками являются: несоответствие типов, неединственность описания каждого идентификатора в программе, отсутствие определения переменной до ее использования и др.

В состав любого компилятора входят три основных компонента: лексический анализатор, синтаксический анализатор, семантический анализатор вместе с генератором машинного кода. Процесс компиляции текста программы в объектный код обычно разбивается на несколько независимых фаз, которые реализуются соответствующими блоками компилятора.

На фазе лексического анализа исходный текст, который рассматривается как несвязный поток символов, разбивается на слова (лексемы). Исходная программа переводится на внутренний язык компилятора, в котором ключевые слова, идентификаторы, метки и константы приведены к одному формату и заменены условными кодами: числовыми или символьными, которые называются дескрипторами. Каждый дескриптор состоит из двух частей: класса (типа) лексемы и указателя на адрес в памяти, где хранится информация о конкретной лексеме. Обычно эта информация организуется в виде таблиц. На фазе лексического анализа проводится лексический контроль – выявление в программе недопустимых слов.

Вторая фаза компиляции – это синтаксический анализ, называемый также грамматическим разбором, на нем проверяется правильность следования операторов. Например, для предложения

IF <ВЫРАЖЕНИЕ> THEN <ПРЕДЛОЖЕНИЕ>;

грамматический разбор состоит в том, чтобы убедиться, что вслед за лексемой IF следует правильное выражение, за этим выражением следует лексема THEN, за которой в свою очередь правильное предложение, оканчивающееся точкой с запятой. Синтаксический анализатор формирует промежуточную программу в форме синтаксического дерева программы.

Генератор кода создает объектный модуль или программу на машинном языке, готовую к выполнению. В качестве входной информации используются выходные таблицы лексического анализатора (таблица идентификаторов, таблица констант и др.) и синтаксическое дерево программы. Синтез объектной программы, как правило, начинается с распределения и выделения памяти для основных программных объектов. Затем по синтаксическому дереву программы производится исследование каждого предложения, и для него генерируются эквиваленты в машинных кодах. Непосредственно генерации объектной программы часто предшествует семантический, или контекстный, анализ, при котором проверяются семантические соглашения в программе.

Между фазами синтаксического анализа и генерацией кодов нередко добавляется фаза оптимизации программы, цель которой состоит в модификации синтаксического дерева программы таким образом, чтобы уменьшить время и сократить объем оперативной памяти, требуемых для выполнения объектного кода программы.

Процессы лексического и синтаксического анализа достаточно хорошо алгоритмизированы. Однако процесс генерации объектного кода автоматизирован не до конца, поэтому создание хорошего генератора объектных программ до сих пор является в некотором роде искусством.

Хотя в состав любого компилятора входят три описанных выше компонента, их взаимодействие может осуществляться разнообразными способами. Наиболее распространенными вариантами являются: одно-, двух- и трехпроходная структура компиляторов (рис.1–3).

 
 


Рис.1. Трехпроходный компилятор. Компилятор фактически состоит из трех программ. Лексический анализатор считывает текст программы и представляет его в форме файла лексем. Синтаксический анализатор читает этот файл и выдает представление программы в виде файла с описанием синтаксического дерева. Наконец, этот файл считывается генератором кода, который создает объектный код программы. Компилятор такого вида назван трехпроходным, так как программа с диска считывается трижды. Недостатки: медленно работает (из-за обращений к диску). Преимущества: нетребователен к объему памяти, гибкость (при модификации компилятора достаточно изменить одну из программ), легко можно добавить программу для оптимизации кодов, вызывая ее между синтаксическим анализатором и генератором кодов.



Рис.2. Однопроходный компилятор. Лексический анализатор и генератор кода являются подпрограммами, которые по необходимости вызывает синтаксический анализатор, который выступает в роли основной управляющей программы. Синтаксический анализатор постоянно обращается к лексическому анализатору, получая от него лексему за лексемой, пока не построит новое предложение, после чего он обращается к генератору кода, который создает объектный код для этого фрагмента программы. Достоинства: быстрота. Недостатки: создаваемый код не оптимален, при загрузке компилятор требует большого объема памяти.

 
 


Рис.3. Двухпроходный компилятор – собрал лучшее от трёх- и однопроходной моделей. Синтаксический анализатор, вызывая лексический анализатор, получает лексему за лексемой и строит файл с синтаксическим деревом программы. Генератор кода считывает этот файл и создает объектный код программы. Достоинства: быстрота, возможность добавить блок оптимизации.

Существует ряд компиляторов, которые выполняются на одной машине, а генерируют объектный код для другой ЭВМ. Эти компиляторы получили название «кроссовых» или кросс-компиляторов (от англ. cross – «перекрестный»). Кросс-компиляторы часто используют для создания программ для небольших специализированных ЭВМ, управляющих работой технологического оборудования и т.п.

Принцип, альтернативный компилированию, реализуется в интерпретаторах. Интерпретаторы отличаются от компиляторов тем, что на последнем этапе вместо генерации объектного кода они сами производят соответствующие действия. Так как реализация интерпретатора часто отличается сравнительной простотой, нередко прибегают к интерпретации вместо компилирования, если скорость выполнения программ не имеет большого значения.

Другие подходы к созданию компилирующих программ заключаются в переводе текста программы на другой язык программирования (например, ассемблер), и последующем вызове стандартного компилятора с этого языка. Такой способ получения объектного кода получил название трансляции. Под транслятором понимают любую программу, которая преобразует строку символов (исходную программу) в другую строку символов (программу в машинных кодах или текст программы на каком-либо другом языке). Поэтому компилятор – это частный случай транслятора, преобразующий текст программы в программу на языке машинных кодов.


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: