какую функцию выполняет компилятор

Национальная библиотека им. Н. Э. Баумана
Bauman National Library

Персональные инструменты

Компилятор

Содержание

Виды компиляторов

Виды компиляции

Структура компилятора

Процесс компиляции состоит из следующих этапов:

В конкретных реализациях компиляторов эти этапы могут быть разделены или, наоборот, совмещены в том или ином виде.

Генерация кода

Генерация машинного кода

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

Результат компиляции — исполнимый модуль — обладает максимальной возможной производительностью, однако привязан к определённой операционной системе и процессору (и не будет работать на других).

Для каждой целевой машины (IBM, Apple, Sun и т. д.) и каждой операционной системы или семейства операционных систем, работающих на целевой машине, требуется написание своего компилятора. Существуют также так называемые кросс-компиляторы, позволяющие на одной машине и в среде одной ОС генерировать код, предназначенный для выполнения на другой целевой машине и/или в среде другой ОС. Кроме того, компиляторы могут оптимизировать код под разные модели из одного семейства процессоров (путём поддержки специфичных для этих моделей особенностей или расширений наборов инструкций). Например, код, скомпилированный под процессоры семейства Pentium, может учитывать особенности распараллеливания инструкций и использовать их специфичные расширения — MMX, SSE и т. п.

Некоторые компиляторы переводят программу с языка высокого уровня не прямо в машинный код, а на язык ассемблера (примером может служить PureBasic, транслирующий бейсик-код в ассемблер FASM). Это делается для упрощения части компилятора, отвечающей за кодогенерацию, и повышения его переносимости (задача окончательной генерации кода и привязки его к требуемой целевой платформе перекладывается на ассемблер), либо для возможности контроля и исправления результата компиляции программистом.

Генерация байт-кода

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

Динамическая компиляция

Из-за необходимости интерпретации байт-код выполняется значительно медленнее машинного кода сравнимой функциональности, однако он более переносим (не зависит от операционной системы и модели процессора). Чтобы ускорить выполнение байт-кода, используется динамическая компиляция, когда виртуальная машина транслирует псевдокод в машинный код непосредственно перед его первым исполнением (и при повторных обращениях к коду исполняется уже скомпилированный вариант).

Декомпиляция

Существуют программы, которые решают обратную задачу — перевод программы с низкоуровневого языка на высокоуровневый. Этот процесс называют декомпиляцией, а такие программы — декомпиляторами. Но поскольку компиляция — это процесс с потерями, точно восстановить исходный код, скажем, на C++, в общем случае невозможно. Более эффективно декомпилируются программы в байт-кодах — например, существует довольно надёжный декомпилятор для Adobe Flash. Разновидностью декомпилирования является дизассемблирование машинного кода в код на языке ассемблера, который почти всегда выполняется успешно (при этом сложность может представлять самомодифицирующийся код или код, в котором собственно код и данные не разделены). Связано это с тем, что между кодами машинных команд и командами ассемблера имеется практически взаимно-однозначное соответствие.

Раздельная компиляция

Раздельная компиляция (англ. separate compilation ) — трансляция частей программы по отдельности с последующим объединением их компоновщиком в единый загрузочный модуль.

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

Появление раздельной компиляции и выделение компоновки как отдельной стадии произошло значительно позже создания компиляторов. В связи с этим вместо термина «компилятор» иногда используют термин «транслятор» как его синоним: либо в старой литературе, либо когда хотят подчеркнуть его способность переводить программу в машинный код (и наоборот, используют термин «компилятор» для подчёркивания способности собирать из многих файлов один).

Интересные факты

На заре развития компьютеров первые компиляторы (трансляторы) называли «программирующими программами» [6] (так как в тот момент программой считался только машинный код, а «программирующая программа» была способна из человеческого текста сделать машинный код, то есть запрограммировать ЭВМ).

Источник

Что такое компилятор

какую функцию выполняет компилятор. Смотреть фото какую функцию выполняет компилятор. Смотреть картинку какую функцию выполняет компилятор. Картинка про какую функцию выполняет компилятор. Фото какую функцию выполняет компилятор

Если вы программист, то наверняка слышали слово “компилятор”. Но знаете ли вы, что это такое на самом деле? Вы когда-нибудь задумывались, что происходит под капотом, когда вы запускаете команду javac (если у вас код на Java)? Вы когда-нибудь хотели создать свой собственный язык программирования? — и просто заводили бесполезный репозиторий GitHub, где все равно есть только один readme.md, потому что вы даже не знаете, с чего начать. Я думаю, что начинать стоит с этого: узнать больше о компиляторе.

Итак, в этой статье мы разберёмся, что представляет собой компилятор. Если вы опытный программист, который знает про компилятор каждую мелочь, то извините, эта статья не для вас. Но если вы — тот самый парень из абзаца выше, то вперёд за мной, в кроличью нору. На протяжении статьи я буду обсуждать следующие подтемы:

Вступление

Компилятор — это не что иное, как переводчик исходного кода.

Задача компилятора — перевести исходный код с одного языка на другой. Это означает, что если вы скормите компилятору исходный код Java, то сможете получить исходный код Python (не самый лучший пример, просто для понимания сути. На самом деле вы получите байт-код Java, который можно запустить на JVM). Для выполнения этого процесса у компилятора есть несколько взаимосвязанных компонентов.

Типы компиляторов

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

Классификация компиляторов в соответствии с этапами компиляции

Здесь мы рассмотрим количество этапов, которые проходит компилятор. Некоторые компиляторы непосредственно преобразуют высокоуровневый исходный код в машинный код, а некоторые — сначала преобразуют высокоуровневый исходный код в промежуточное представление перед преобразованием в машинный код.

Таким образом, в соответствии с этой классификацией можно выделить три типа компиляторов:

Если вы хотите узнать больше об этой классификации компиляторов, посмотрите сюда.

Классификация компиляторов в соответствии с исходным кодом и целевым кодом

Для преобразования исходного кода в целевой применяются разные подходы. Некоторые компиляторы преобразуют код на высокоуровневом языке в машинный. Некоторые компиляторы преобразуют с одного языка высокого уровня на другой язык высокого уровня. Таким образом, здесь выделяются следующие типы:

Архитектура компилятора

Когда компилятор компилирует (переводит) исходный код, он проходит несколько этапов:

Мы можем разделить все эти этапы на две фазы, примерно как фронтенд и бэкенд. Эти фазы включают в себя следующие этапы:

Фронтенд

Бэкенд

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

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

Теперь вы знаете, что компилятор — это программа, которая преобразует исходный код в другой исходный код. Компилятор получает исходный код в виде файла. Этот файл содержит код в текстовом формате, но компилятор не может работать с этим текстом. Необходимо преобразовать этот текст в некоторый другой формат, понятный компилятору. Для этого компилятор разбивает текст по маркерам. Помните, что эти маркеры заранее определены в грамматике языка. Маркеры пригодятся на следующих этапах процесса компиляции:

какую функцию выполняет компилятор. Смотреть фото какую функцию выполняет компилятор. Смотреть картинку какую функцию выполняет компилятор. Картинка про какую функцию выполняет компилятор. Фото какую функцию выполняет компилятор

KEYWORD, BRACKET, IDENTIFIER, OPERATOR, NUMBER на приведенной выше диаграмме — это и есть маркеры. Компилятор использует лексический анализ для идентификации маркеров, и если он получает маркер, который не определен заранее в грамматике языка, то это будет считаться ошибкой.

Синтаксический анализ (парсинг)

На этом этапе компилятор проверяет, расположены ли идентифицированные ранее маркеры в правильном порядке. Для этого в каждом языке есть набор правил, называемый грамматикой. Во-первых, компилятор пытается построить структуру данных — дерево синтаксического анализа. Если компилятор смог успешно построить дерево синтаксического анализа в соответствии с заранее определенными правилами грамматики, то в исходном коде нет синтаксических ошибок. В противном случае возникают ошибки и компилятор их покажет.

какую функцию выполняет компилятор. Смотреть фото какую функцию выполняет компилятор. Смотреть картинку какую функцию выполняет компилятор. Картинка про какую функцию выполняет компилятор. Фото какую функцию выполняет компилятор

Здесь мы сначала определили грамматику. Затем компилятор пытается построить дерево синтаксического анализа для исходного кода 2 + 3 * 3. В этом случае компилятору удается построить дерево синтаксического анализа (с правой стороны) в соответствии с грамматикой, следовательно в этой программе нет синтаксических ошибок.

Семантический анализ

Просто потому, что программа не содержит синтаксических ошибок, код еще не может считаться правильным. Рассмотрим предложение ниже.

I love compilers

какую функцию выполняет компилятор. Смотреть фото какую функцию выполняет компилятор. Смотреть картинку какую функцию выполняет компилятор. Картинка про какую функцию выполняет компилятор. Фото какую функцию выполняет компилятор

Компилятор при анализе синтаксиса может решить, что в этом предложении нет синтаксических ошибок, потому что маркеры (слова) расположены в правильном порядке.

Теперь рассмотрим предложение ниже.

I eat compilers

какую функцию выполняет компилятор. Смотреть фото какую функцию выполняет компилятор. Смотреть картинку какую функцию выполняет компилятор. Картинка про какую функцию выполняет компилятор. Фото какую функцию выполняет компилятор

Предположим, что eat — правильный маркер в соответствии с грамматикой. Таким образом, предложение признается правильным на этапе лексического и синтаксического анализа, поскольку слова расположены в правильном порядке. Но в этом предложении нет никакого смысла — никто не может есть компиляторы.

Итак, согласно этапу семантического анализа, эта программа содержит ошибку. Мы называем эту разновидность ошибок семантическими ошибками. Взгляните на этот простой Java-код:

Здесь нет синтаксических ошибок. Все маркеры упорядочены правильно. Но на пятой строке int total = c + d — не имеет никакого значения, так как идентификаторы c и d не определены. Это и есть семантическая ошибка.

Генерация промежуточного кода

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

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

какую функцию выполняет компилятор. Смотреть фото какую функцию выполняет компилятор. Смотреть картинку какую функцию выполняет компилятор. Картинка про какую функцию выполняет компилятор. Фото какую функцию выполняет компилятор

Существует два основных типа промежуточных представлений:

Существует также несколько способов представления промежуточного представления.

Оптимизация кода

Этап оптимизации кода выполняет две основные задачи: минимизация времени или минимизация ресурсов. Что все это значит? Когда пользователь пишет код, нет ничего, кроме инструкций. Когда процессор выполняет эти инструкции, требуют время и ресурсы памяти. Таким образом, целью этапа оптимизации кода становится сокращение времени выполнения и ресурсов, потребляемых программой. Оптимизатор кода всегда следует трем правилам:

Существует два способа оптимизации кода:

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

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

Генерация кода

Генерация кода — это последний этап процесса компиляции. Да, после может следовать машинно-зависимая оптимизация кода. Но мы можем рассматривать и то, и другое вместе как генерацию кода. На этом этапе компилятор генерирует машинно-зависимый код. Генератор кода должен иметь представление о среде выполнения целевой машины и ее наборе команд.

На этом этапе компилятор выполняет несколько основных задач:

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

В этой статье я предоставляю только краткое описание. Если вам хочется углубиться в эти концепции, к вашим услугам миллионы ресурсов в интернете.

Источник

Что такое компилятор?

какую функцию выполняет компилятор. Смотреть фото какую функцию выполняет компилятор. Смотреть картинку какую функцию выполняет компилятор. Картинка про какую функцию выполняет компилятор. Фото какую функцию выполняет компилятор

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

Зачем нужен компилятор?

Процессор — самая важная часть компьютера. Он обрабатывает информацию, выполняет команды пользователя и следит за работой всех подключенных устройств. Но процессор может разобрать только машинный код — набор 0 и 1, которые записаны в определённом порядке.

Почему именно 0 и 1? В процессор поступают электрические сигналы. Сильный сигнал обозначается цифрой 1, а слабый — 0. Набор таких цифр обозначает какую-то команду. Процессор ее распознает и выполняет.

Программы для первых компьютеров выглядели как огромные наборы 0 и 1. Чтобы записать такую программу, инженеры пользовались гибкими картонными карточками — перфокартами. Цифры на перфокарте записывались поочередно, в несколько строк. Чтобы записать 1, программист делал отверстие в карте. Места без отверстия обозначали 0.

какую функцию выполняет компилятор. Смотреть фото какую функцию выполняет компилятор. Смотреть картинку какую функцию выполняет компилятор. Картинка про какую функцию выполняет компилятор. Фото какую функцию выполняет компилятор

Компьютер считывал перфокарту специальным устройством и выполнял записанную команду. Для одной программы составляли сотни перфокарт.

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

какую функцию выполняет компилятор. Смотреть фото какую функцию выполняет компилятор. Смотреть картинку какую функцию выполняет компилятор. Картинка про какую функцию выполняет компилятор. Фото какую функцию выполняет компилятор

Как работает компилятор?

Преобразование программного кода в машинный называется компиляцией. Компиляция только преобразует код. Она не запускает его на исполнение. В этот момент он “статически” (то есть без запуска) транслируется в машинный код. Это сложный процесс, в котором сначала текст программы разбирается на части и анализируется, а затем генерируется код, понятный процессору.

какую функцию выполняет компилятор. Смотреть фото какую функцию выполняет компилятор. Смотреть картинку какую функцию выполняет компилятор. Картинка про какую функцию выполняет компилятор. Фото какую функцию выполняет компилятор

Разберём этапы компиляции на примере вычисления периметра прямоугольника:

После запуска программы компилятору нужно определить, какие команды в ней записаны. Сначала компилятор разделяет программу на слова и знаки — токены, и записывает их в список. Такой процесс называется лексическим анализом. Его главная задача — получить токены.

Компилятор должен понять, какие токены в списке связаны с токен-оператором. Чтобы сделать это правильно, для каждого оператора строится специальная структура — логическое дерево или дерево разбора.

Так операция P = 2*(a + b) будет преобразована в логическое дерево:

какую функцию выполняет компилятор. Смотреть фото какую функцию выполняет компилятор. Смотреть картинку какую функцию выполняет компилятор. Картинка про какую функцию выполняет компилятор. Фото какую функцию выполняет компилятор

Теперь каждое дерево нужно разобрать на команды, и каждую команду преобразовать в машинный код. Компилятор начинает читать дерево снизу вверх и составляет список команд:

Компилятор еще раз проверяет команды, находит ошибки и старается улучшить код. При успешном завершении этого этапа, компилятор переводит каждую команду в набор 0 и 1. Наборы записываются в файл, который сможет прочитать и выполнить процессор.

На чем написан компилятор?

В 1950-е годы группа разработчиков IBM под руководством Джона Бэкуса разработала первый высокоуровневый язык программирования Fortran, который позволил писать программы на понятном человеку языке. Помимо языка, инженеры работали и над компилятором. Он представлял собой программу с набором исполняемых команд, которая могла компилировать другие программы на Fortran, в том числе и улучшенную версию себя.

В дальнейшем язык Fortran и его компилятор использовали, чтобы написать компиляторы для новых языков программирования. Такой подход используют программисты и в настоящее время. Писать машинный код долго и неудобно. К тому же, для современных процессоров он может отличаться. Придется писать несколько версий одного и того же компилятора для разных компьютеров. Быстрее и проще написать компилятор на существующем языке программирования. Для этого разработчики выбирают удобный язык и пишут на нем первую версию своего компилятора. Он будет более универсальным для компьютеров и легко скомпилирует улучшенную версию себя. какую функцию выполняет компилятор. Смотреть фото какую функцию выполняет компилятор. Смотреть картинку какую функцию выполняет компилятор. Картинка про какую функцию выполняет компилятор. Фото какую функцию выполняет компилятор

Какие бывают компиляторы?

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

Дело в том, что современные процессоры отличаются друг от друга устройством, поэтому машинный код для одного процессора будет понятен, а для другого нет. Это касается и операционных систем: одна и та же программа будет работать на Windows, но не запустится на Linux или MacOS. Поэтому нужно пользоваться тем компилятором, который работает с нужным процессором и операционной системой.

Если программа будет работать на нескольких операционных системах, то нужен кросс-компилятор — компилятор, который преобразует универсальный машинный код. Например, GNU Compiler Collection(сокращенно GCC) поддерживает C++, Objective-C, Java, Фортран, Ada, Go и поддерживает разную архитектуру процессоров.

Начинающие программисты даже не знают о наличии компилятора на компьютере. Они пишут программы в интегрированной среде разработки, в которую встроен компилятор, а иногда и не один. В этом случае, выбор компилятора делает среда, а не программист. Например, MS Visual Studio поддерживает компиляторы для операционных систем Windows, Linux, Android. Выбирая тип проекта, Visual Studio определяет процессор и операционную систему компьютера, и после этого выбирает подходящий компилятор.

Какие ошибки может определить компилятор?

Когда компилятор анализирует текст программы, он проверяет, соответствует ли запись оператора стандартам языка. Если найдено несоответствие, то компилятор выводит об этом информацию пользователю в виде ошибки. Когда вся программа разобрана, пользователь видит список ошибок, которые есть в коде, и может их исправить. Пока программист не исправит ошибки, компилятор не перейдет к следующему этапу — генерации машинного кода для процессора. Чаще всего компилятор показывает пользователю:

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

Выводы и рекомендации

Компилятор — переводчик между программистом и процессором. Он преобразует текст программы в машинный код, определяет ряд ошибок в программе и оптимизирует ее работу. Выбирая, где компилировать программу, важно помнить о том, что машинный код для процессоров и операционных систем будет разным, и подобрать правильный компилятор. Чем точнее компилятор определит команды, тем корректнее и быстрее будет работать программа. Для этого следуйте простым рекомендациям:

Источник

Системы программирования.

Системы программирования

Системой программирования будем называть комплекс программных средств, предназначенных для кодирования, тестирования и отладки программного обеспечения.

Другими словами, это набор специализированных программных продуктов, которые являются инструментальными средствами разработчика.

Система программирования, как правило, включает следующие программные компоненты:

· транслятор с соответствующего языка;

· компоновщик (редактор связей);

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

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

Трансляторы предназначены для преобразования программ, написанных на языках программирования, в программы на машинном языке.

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

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

Трансляторы делятся на два класса: компиляторы и интерпретаторы.

Компилятор переводит весь исходный модуль на машинный язык.

Интерпретатор последовательно переводит на машинный язык каждый оператор исходного модуля и сразу же выполняет его.

Соответственно говорят о компилируемых и интерпретируемых языках программирования.

Загрузочный модуль может быть помещен операционной системой в оперативную память и выполнен.

Базовый набор функций отладчика включает:

· пошаговое выполнение программы (режим трассировки) с отображением результатов,

· остановка в заранее определенных точках,

· возможность остановки в некотором месте программы при выполнении некоторого условия;

· изображение и изменение значений переменных.

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

Примерами современных систем программирования являются системы программирования

Microsoft Visual Basic,

Microsoft Visual C ++

и многие-многие другие.

Основные сведения о компиляции

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

Кроме основного компилятора, большинство систем программирования могут содержать в своем составе целый ряд других компиляторов. Так, большинство систем содержат компилятор с языка Assembler и компилятор с входного языка описания ресурсов. Но они редко непосредственно взаимодействуют с пользователем.

Основные термины и понятия.

Транслятор – это программа, которая переводит входную программу на исходном (входном) языке в эквивалентную ей выходную программу на результирующем (выходном) языке.

Близко по смыслу к этому понятию понятие компилятор.

Компилятор – это транслятор, который осуществляет перевод исходной программы в эквивалентную ей объектную программу на языке машинных команд или языке ассемблера.

Таким образом, компилятор отличается от транслятора тем, что его результирующая программа написана обязательно на языке машинных команд или языке ассемблера. Результирующая программа транслятора в общем случае может быть написана на любом языке (например, транслятор с языка Pascal на язык С).

Таким образом, компиляторы – это вид трансляторов.

Повторим еще раз принципиально отличное понятие «интерпретатор».

Интерпретатор – это программа, которая воспринимает входную программу на исходном языке и выполняет ее. (Интерпретатор не порождает результирующую программу и никакого результирующего кода.)

Основные блоки (фазы) компилятора, их функции

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

В процессе компиляции обычно выделяют следующие подпроцессы (блоки, этапы).

1. Лексический анализ.

2. Работа с таблицами.

3. Синтаксический анализ, или разбор.

4. Генерация кода, или трансляция в промежуточный код (например, языка ассемблер).

5. Оптимизация кода.

6. Генерация объектного кода.

Замечание. В конкретных компиляторах порядок может несколько отличаться, а некоторые блоки могут объединяться в один. В реальном компиляторе блоки не обязательно разделены.

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

Входом является цепочка символов некоторого алфавита.

Некоторые комбинации символов в программе рассматриваются как единые объекты – лексемы (например, зарезервированные слова, идентификаторы, числовые константы).

Работа лексического анализатора состоит в том, чтобы сгруппировать определенные символы в единые синтаксические объекты – лексемы.

Выходом является последовательность лексем.

Например, в результате лексического анализа следующей цепочки символов

с ost:= (price + tax) * 0.98

будет обнаружено, что

Работа с таблицами

Информация о лексемах собирается и записывается в одну или несколько таблиц, например, в виде списка лексем и соответствующей информации о них.

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

Вход – цепочка лексем.

На этом этапе исследуется цепочка лексем и устанавливается, удовлетворяет ли она структурным условиям, явно сформулированным в определении синтаксиса языка.

Выходом анализатора является дерево, которое представляет синтаксическую структуру, присущую исходной программе.

Генерация кода

Полученное дерево используется для перевода входной программы в программу на машинном языке, но чаще осуществляется перевод на промежуточный язык (ассемблер).

Замечание. На практике чаще одновременно строится и дерево, и код.

Существует несколько методов построения промежуточного кода по синтаксическому дереву. Основным из них является синтаксически управляемый перевод (трансляция).

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

Оптимизация кода

На этом этапе производится попытка сделать объектные программы более эффективными (т.е. быстрее работающими или более компактными).

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

Еще одним методом оптимизации кода является оптимизация вычисления логических выражений (не всегда полностью надо выполнять вычисление всего выражения, чтобы знать его результат, иногда по значению одного операнда можно определить значение всего выражения).

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

Для оптимизации циклов используются следующие методы: вынесение инвариантных вычислений из циклов (вынесение тех операций, операнды которых не изменяются); замена операций с индуктивными переменными (изменение сложных операций с переменными, значения которых в процессе выполнения цикла образуют арифметическую прогрессию, на более простые операции); слияние и развертывание циклов (слияние двух вложенных циклов в один и замена цикла на линейную последовательность операций).

Генерация объектного кода

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

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

Compile successful. Press any key.

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

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *