И.А. Волкова, И.Г. Головин, Л.Е. Карпов - Системы программирования (1119414), страница 8
Текст из файла (страница 8)
Даже в ассемблерах можно встретитьфрагменты, выполняющие в том или ином виде распределение памяти и регистров. Темболее, подобные действия по формированию зон или блоков памяти, определениюсмещений в этих зонах, приписке регистров некоторым элементам данных,необходимы в компиляторах.
При проведении таких действий производитсякомпоновка данных в блоки, выравнивание данных на границы физических элементовпамяти (байтов, слов, страниц), а также по регистрам специального назначения(векторным регистрам, регистрам устройства работы с вещественными числами). Болеедетально распределение памяти рассматривается в разделе 3.3.5.Генерация команд и машинно-зависимая оптимизация.
Этап генерациикоманд (кода) проводится в компиляторах, однако иногда этот этап присутствует и винтерпретаторах. На этом этапе производится окончательное преобразованиевнутреннего представления транслируемой программы к записи на машинном языкеили на языке ассемблера.В интерпретаторе (точнее в трансляторе со смешанной стратегией трансляции)эта часть заменяется программой, которая интерпретирует внутреннее представлениеисходной программы. Однако возникновение программы, готовой к интерпретации иливыполнению в результате работы только компилятора, возможно не всегда. Многиесовременные языки, среди которых Си, Си++, Java, предлагают другую концепциюпрограммы, основанную на понятии “единицы трансляции”. Использование этихязыков предполагает, что при запуске компилятора компилируется только некотораячасть полной программы, а остальные части добавляются к ней по мере готовностидругими компонентами системы программирования, например, редактором связей.В подобных случаях интерпретацию программы также нельзя проводитьнепосредственно после ее компиляции.
Необходимо сначала подключить к программенедостающие фрагменты, одни из которых (может быть) надо сначалаоткомпилировать, а другие (может быть) добавить из набора уже откомпилированныхпрограмм, либо из библиотек.Более детально проблемы генерации кода рассматриваются в разделе 3.3.6.3.3.1.2. Однопроходный компиляторРазобранная схема работы компилятора является концептуальной.
Многиекомпиляторы, однако, построены с отступлениями (иногда значительными) отрассмотренной схемы. Фазы компиляции могут разбиваться на отдельныесоставляющие или, напротив, объединяться друг с другом. Может даже менятьсяпорядок их выполнения.Выбор той или иной схемы определяется многими обстоятельствами. Одним изкритериев является объем доступной оперативной памяти.
Если памяти недостаточно,разработчикам приходится разбивать процесс компиляции на части, передаваяинформацию от одной части к другой через внешнюю память, в которую записываетсяпромежуточное представление транслируемой программы. Существуют и другиекритерии, например, планируемая скорость работы транслятора, степень оптимизациипрограмм.При выполнении каждого прохода компилятору доступна вся информация,накопленная в информационных таблицах на предыдущих проходах. При выполнении30очередного прохода компилятор может также вновь обратиться к исходному текступрограммы.
Пользователю становятся доступными только результаты самогопоследнего прохода – в виде объектной программы, сформированной компилятором,никакие промежуточные результаты компиляции пользователю не видны.Поскольку процедуры чтения из внешней памяти и записи на внешнюю памятьимеют относительно невысокую скорость, разработчики компиляторов всегдастремятся уменьшить количество проходов в своих компиляторах. Для языковпрограммирования, которые строились с учетом возможного упрощения процессатрансляции, удается строить такую схему построения компилятора:Начальные установкиИсходнаяпрограммаТекстОбращение залексемойСинтаксическийанализаторЛексемаСинтаксическая конструкцияВозвратуправленияЛексическийанализаторЗавершениеформированияпрограммыСемантический анализатор,распределитель памяти,генератор командТекст или кодОбъектная программаЛексический анализатор в данном случае работает как сопрограмма дляпрограммы синтаксического анализатора.
Аналогично может работать программасемантического анализа и генератора команд.В современных компиляторах лексический и синтаксический анализаторы – этовзаимосвязанные части общего процесса. Возможны два принципиально различныхметода организации взаимосвязи лексического и синтаксического анализа –последовательный и параллельный.При последовательном варианте лексический анализатор просматривает весьтекст исходной программы от начала до конца и преобразует его в таблицу лексем.Таблица лексем заполняется вся и полностью, компилятор использует ее дляпоследующих фаз компиляции, но не изменяет. Если в процессе работы лексическийанализатор не смог правильно определить тип лексемы, считается, что программасодержит ошибку.
Получающийся в данном случае компилятор никогда не может бытьоднопроходным.Последовательная работа лексического и синтаксического анализаторовпредставляет собой самый простой вариант их взаимодействия. Она проще в31реализации и в определенных условиях обеспечивает более высокую скорость работыкомпилятора, чем параллельное взаимодействие.ИдентификаторыЛексическийанализатор(сканер)ИсходнаяпрограммаТаблицаидентификаторов(таблица имен)ЛексемыСинтаксическийанализаторОбращение залексемойТаблица лексемОчереднаялексема иидентификаторыПри параллельном варианте лексический анализ текста исходной программывыполняется поэтапно. Лексический анализатор выделяет очередную лексему висходном тексте и сразу передает ее синтаксическому анализатору.
После того, каксинтаксический анализатор успешно выполнит разбор очередной законченнойконструкции исходного языка (обычно такой конструкцией является отдельныйоператор исходного языка), лексический анализатор помещает найденные лексемы втаблицу лексем и в таблицу идентификаторов, а затем продолжает разбор дальше в томже порядке.
Параллельное взаимодействие лексического и синтаксическогоанализаторов строится по такой схеме:ИдентификаторыЛексическийанализатор(сканер)ИсходнаяпрограммаОчереднаялексемаТаблицаидентификаторов(таблица имен)Обращение залексемойСинтаксическийанализаторПреобразование входного языка. Вторая задача лексического анализатора естьвыполнение действий, связанных с обнаружением и распознаванием той или инойлексемы. Фактически ее решение приводит к тому, что с конечным автоматом,лежащим в основе лексического анализатора, ассоциируют не только входной язык, нои выходной.
Автомат должен не только распознать правильную лексему на входе, но ипородить связанную с ней последовательность символов на выходе. Тем самым,конечный автомат превращается в конечный преобразователь. Однако лексическийанализатор не может действовать, как простой преобразователь, его задача шире, чемтолько порождение цепочки символов выходного языка. Он должен уметь выполнятьтакие действия, как запись выделенной лексемы в таблицу лексем, поиск ее в таблице32идентификаторов или в таблице констант, а также запись нового имени или новойконстанты в соответствующую таблицу.
Часто подобные действия выполняютсянепосредственно при обнаружении лексемы в исходном тексте.Выбирая тот или иной способ представления таблиц в программах компилятора,следует руководствоваться следующими требованиями к ним:•••Структура таблиц должна обеспечивать эффективность поиска в таблицах;Структура таблиц должна обеспечивать эффективность вставок в таблицы(имеются в виду, как вставки новых элементов, так и вставки новойинформации в ранее имевшиеся записи);Структура таблиц должна обеспечивать возможность динамического ростаобъемов таблиц.Влияет на программу реального лексического анализатора и необходимостьотслеживать возможные ошибки в тексте исходных программ. Анализатор долженпринимать меры для максимально более полной локализации ошибок, причем нетолько лексических, но также и синтаксических и семантических.3.3.2. Задачи семантического анализаСемантический анализ пользуется всеми результатами предыдущих стадийкомпиляции.
Со стороны лексического анализатора ему передаются все созданныетаблицы (идентификаторов, констант и т. д.), а со стороны синтаксическогоанализатора – результаты синтаксического разбора конструкций языка. Эти результатыпредставляются на выходе синтаксического анализатора в одной из форм внутреннегопредставления программ в компиляторе. Обычно на этапе семантического анализаиспользуются некоторые варианты синтаксических деревьев, построенных в результатесинтаксического разбора. Такое древовидное представление программы удобно дляпроведения семантического анализа потому, что для анализа семантикикомпилируемой программы важно знать именно общую структуру этой программы.Семантический анализ в свою очередь тоже может разделяться на отдельныестадии.
Одна из них вполне может совмещаться с синтаксическим анализом ипроводится параллельно с ним. Другая стадия выполняется позднее, когда завершенсинтаксический анализ последней конструкции программы и начинается подготовка кгенерации объектной программы. Первая часть выполняется после завершениясинтаксического анализа очередной конструкции входного языка (процедуры,функции, блока операторов и т. п.) на основе имеющихся в информационных таблицахданных. Вторая часть связана с проведением полного семантического анализа всейпрограммы.Независимо от выбранного способа реализации основная работа семантическогоанализатора связана•••с проверкой соблюдения во входной программе семантических соглашений иконтекстных условий входного языка,с включением во внутреннее представление компилируемой программыдополнительных операторов, связанных с семантикой входного языка,с проверкой семантических (смысловых) норм языка, напрямую несвязанных с входным языком и его синтаксисом.33Проверки,выполняемыесемантическиманализатором,называютсястатическими проверками.
Они отличаются от динамических проверок, выполняемых впроцессе работы программы.3.3.2.1. Проверка контекстных условийПо виду (синтаксической записи) большинства операторов нельзя утверждать ихсемантическую правильность или ошибочность. Проверка семантических соглашений иконтекстных условий заключается в сопоставлении входных цепочек исходнойпрограммы с требованиями семантики языка программирования. Такие соглашенияесть в каждом языке, проверять их на этапе синтаксического анализа невозможно.Обычными видами статических проверок в компиляторах являются1. Проверки типов.