Трансляторы и их виды. Трансляторы В виде каких двух разновидностей выполняются трансляторы

  • 04.02.2023

Транслятор обычно выполняет также диагностику ошибок, форирует словари идентификаторов, выдаёт для печати тексты программы и т. д.

Трансляция программы - преобразование программы, представленной на одном из языков программирования , в программу на другом языке и, в определённом смысле, равносильную первой.

Язык, на котором представлена входная программа, называется исходным языком , а сама программа - исходным кодом . Выходной язык называется целевым языком или объектным кодом .

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

Виды трансляторов

  • Адресный . Функциональное устройство, преобразующее виртуальный адрес (Virtual address) в реальный адрес памяти (Memory address).
  • Диалоговый . Обеспечивает использование языка программирования в режиме разделения времени .
  • Многопроходной . Формирует объектный модуль за несколько просмотров исходной программы.
  • Обратный . То же, что детранслятор . См. также: декомпилятор , дизассемблер .
  • Однопроходной . Формирует объектный модуль за один последовательный просмотр исходной программы.
  • Оптимизирующий . Выполняет оптимизацию кода в создаваемом объектном модуле.
  • Синтаксически-ориентированный (синтаксически-управляемый) . Получает на вход описание синтаксиса и семантики языка и текст на описанном языке, который и транслируется в соответствии с заданным описанием.
  • Тестовый . Набор макрокоманд языка ассемблера , позволяющих задавать различные отладочные процедуры в программах, составленных на языке ассемблера.

Реализации

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

Можно привести ряд других примеров, в которых архитектура разработанных серий вычислительных машин базировалась или сильно зависела от некоторой модели структуры программы. Так, серия GE/Honeywell Multics основывалась на семантической модели выполнения программ, написанных на языке ПЛ/1 . В Шаблон:Не переведено B5500, B6700 … B7800 прототипом послужила модель программы этапа выполнения, написанной на расширенном языке Алгол . …

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

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

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

Противоположный метод реализации - когда программа исполняется с помощью интерпретатора вообще без трансляции. Интерпретатор программно моделирует машину, цикл выборки-исполнения которой работает с командами на языках высокого уровня, а не с машинными командами. Такое программное моделирование создаёт виртуальную машину , реализующую язык. Этот подход называется чистой интерпретацией . Чистая интерпретация применяется как правило для языков с простой структурой (например, АПЛ или Лисп). Интерпретаторы командной строки обрабатывают команды в скриптах в UNIX или в пакетных файлах (.bat) в MS-DOS также как правило в режиме чистой интерпретации.

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

Существуют компромиссные между компиляцией и чистой интерпретацией варианты реализации языков программирования, когда интерпретатор перед исполнением программы транслирует её на промежуточный язык (например, в байт-код или p-код), более удобный для интерпретации (то есть речь идёт об интерпретаторе со встроенным транслятором). Такой метод называется смешанной реализацией . Примером смешанной реализации языка может служить Perl . Этот подход сочетает как достоинства компилятора и интерпретатора (бо́льшая скорость исполнения и удобство использования), так и недостатки (для трансляции и хранения программы на промежуточном языке требуются дополнительные ресурсы; для исполнения программы на целевой машине должен быть представлен интерпретатор). Также, как и в случае компилятора, смешанная реализация требует, чтобы перед исполнением исходный код не содержал ошибок (лексических, синтаксических и семантических).

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

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

Этот метод хорошо подходит для

РАЗДЕЛ 7. Трансляция, компиляция и интерпретация

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

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

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

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

Транслятор обычно выполняет также диагностику ошибок, формирует словари идентификаторов, выдаёт для печати тексты программы и т.д.

Трансляция программы - преобразование программы, представленной на одном из языков программирования, в программу на другом языке и, в определённом смысле, равносильную первой.

Язык, на котором представлена входная программа, называется исходным языком , а сама программа - исходным кодом . Выходной язык называется целевым языком или объектным кодом .

Виды трансляторов

Трансляторы подразделяют:

· Адресный . Функциональное устройство, преобразующее виртуальный адрес (англ. Virtual address ) в реальный адрес (англ. Memory address ).

· Диалоговый . Обеспечивает использование языка программирования в режиме разделения времени.

· Многопроходной . Формирует объектный модуль за несколько просмотров исходной программы.

· Обратный . То же, что детранслятор. См. также: декомпилятор, дизассемблер.

· Однопроходной . Формирует объектный модуль за один последовательный просмотр исходной программы.

· Оптимизирующий . Выполняет оптимизацию кода в создаваемом объектном модуле.

· Синтаксически-ориентированный (синтаксически-управляемый) . Получает на вход описание синтаксиса и семантики языка и текст на описанном языке, который и транслируется в соответствии с заданным описанием.

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



Трансляторы реализуются в виде компиляторов или интерпретаторов . С точки зрения выполнения работы компилятор и интерпретатор существенно различаются.

Компиля́тор (англ. compiler - составитель, собиратель) -транслятор, выполняющий преобразование программы, составленной на исходном языке, в объектный модуль. Программа, переводящая текст программы на языке высокого уровня, в эквивалентную программу на машинном языке.

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

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

Компилировать - проводить трансляцию машинной программы с проблемно-ориентированного языка на машинно-ориентированный язык.

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

Интерпретатор (англ. interpreter - истолкователь, устный переводчик) переводит и выполняет программу строка за строкой . Интерпретатор берёт очередной оператор языка из текста программы, анализирует его структуру и затем сразу исполняет (обычно после анализа оператор транслируется в некоторое промежуточное представление или даже машинный код для более эффективного дальнейшего исполнения). Только после того как текущий оператор успешно выполнен, интерпретатор перейдёт к следующему. При этом если один и тот же оператор будет выполняться в программе многократно, интерпретатор будет выполнять его так как, как будто встретил впервые. Вследствие этого программы, в которых требуется осуществить большой объём вычислений, будут выполняться медленно. Кроме того, для выполнения программы на другом компьютере там тоже должен стоять интерпретатор – ведь без него текст является просто набором символов.



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

Различия между компиляцией и интерпретацией.

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

2. Откомпилированные программы работают быстрее, но интерпретируемые проще исправлять и изменять.

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

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

Практически все языки программирования низкого уровня и третьего поколения, вроде ассемблера, Си или Модулы-2, являются компилируемыми, а более высокоуровневые языки, вроде Python или SQL, - интерпретируемыми.

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

4. Трансляция и интерпретация - разные процессы: трансляция занимается переводом программ с одного языка на другой, а интерпретация отвечает за исполнение программ. Однако, поскольку целью трансляции как правило является подготовка программы к интерпретации, то эти процессы обычно рассматриваются вместе.

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

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


Процесс компиляции разделяется на несколько этапов:

1. Препроцессор. Исходная программа обрабатывается путём подстановки имеющихся макросов и заголовочных файлов.

2. Лексический и синтаксический анализ. Программа преобразовывается в цепочку лексем, а затем во внутреннее представление в виде дерева.

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

4. Генерация кода. Внутреннее представление преобразовывается в блоки команд процессора, которые преобразовываются в ассемблеровский текст или в объектный код.

5. Ассемблирование. Если генерируется ассемблерный текст, производится его ассемблирование с целью получения объектного кода.

6. Сборка. Сборщик соединяет несколько объектных файлов в исполняемый файл или библиотеку.

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

На этапе ЛА обнаруживаются некоторые (простейшие) ошибки (недопустимые символы, неправильная запись чисел, идентификаторов и др.).

Рассмотрим более подробно стадию лексического анализа.

Основная задача лексического анализа - разбить входной текст, состоящий из последовательности одиночных символов, на последовательность слов, или лексем, т.е. выделить эти слова из непрерывной последовательности символов. Все символы входной последовательности с этой точки зрения разделяются на символы, принадлежащие каким-либо лексемам, и символы, разделяющие лексемы (разделители). В некоторых случаях между лексемами может и не быть разделителей. С другой стороны, в некоторых языках лексемы могут содержать незначащие символы (например, символ пробела в Фортране). В Си разделительное значение символов-разделителей может блокироваться («\» в конце строки внутри «...»).

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

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

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

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

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

Рис. 3.1:

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

Основная задача синтаксического анализа - разбор структуры программы. Как правило, под структурой понимается дерево, соответствующее разбору в контекстно-свободной грамматике языка. В настоящее время чаще всего используется либо LL(1) - анализ (и его вариант - рекурсивный спуск), либо LR(1)-анализ и его варианты (LR(0), SLR(1), LALR(1) и другие). Рекурсивный спуск чаще используется при ручном программировании синтаксического анализатора, LR(1) - при использовании систем автоматизации построения синтаксических анализаторов.

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

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

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

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

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

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

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

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

  • Однопроходной объектный модуль при однократном последовательном чтении исходного кода (за один проход).
  • Многопроходной транслятор - транслятор, создающий объектный модуль после нескольких чтений исходного кода (за несколько проходов).
  • Оптимизирующий транслятор - транслятор, выполняющий оптимизацию создаваемого кода перед записью в объектный файл . См. оптимизирующий компилятор .
  • Тестовый транслятор - транслятор, получающий на вход исходный код и выдающий на выходе изменённый исходный код . Запускается перед основным транслятором для добавления в исходный код отладочных процедур . Например, транслятор с языка ассемблера может выполнять замену макрокоманд на код.
  • Обратный транслятор - транслятор, выполняющий преобразование машинного кода в текст на каком-либо языке программирования . См. дизассемблер , декомпилятор .
  • Реализации

    Цель трансляции - преобразование текста с одного языка на язык, понятный адресату. При трансляции компьютерной программы адресатом может быть:

    • устройство - процессор (трансляция называется компиляцией);
    • программа - интерпретатор (трансляция называется интерпретацией).

    Виды трансляции:

    Компиляция

    Процесс компиляции, как правило, состоит из нескольких этапов:

    • создание на основе результатов анализов промежуточного кода;
    • оптимизация промежуточного кода;
    • создание объектного кода , в данном случае машинного .

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

    • по запросу пользователя в ядре операционной системы создаётся объект «процесс »;
    • загрузчик программ операционной системы выполняет следующие действия:
    • читает исполняемый файл ;
    • загружает его в память ;
    • загружает в память динамические библиотеки ;
    • выполняет связывание машинного кода программы с динамическими библиотеками (динамическое связывание);
    • передаёт управление программе.

    Достоинства компиляции:

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

    Недостатки компиляции:

    • компиляция - медленный процесс;
    • при внесении изменений в исходный код, требуется повторная компиляция.

    Интерпретация

    Интерпретация - процесс чтения и выполнения исходного кода . Реализуется программой - интерпретатором .

    Интерпретатор может работать двумя способами:

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

    Этапы работы интерпретатора:

    • создание промежуточного представления кода (при чистой интерпретации не выполняется);
    • исполнение.

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

    Чистая интерпретация применяется, обычно, для языков с простой структурой, например, языков сценариев , языков АПЛ и Лисп .

    Примеры интерпретаторов, создающих байт-код : Perl , PHP , Python , Ruby , Erlang .

    Достоинства интерпретаторов по сравнению с компиляторами:

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

    Недостатки интерпретаторов по сравнению с компиляторами:

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

    Сравнение чистого интерпретатора и интерпретатора, создающего байт-код :

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

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

    Динамическая или JIT компиляция - трансляция, при которой исходный или промежуточный код преобразуется (компилируется) в машинный код непосредственно во время исполнения, «на лету» (англ. just in time , JIT ). Компиляция каждого участка кода выполняется только один раз; скомпилированный код сохраняется в кеше и при необходимости используется повторно.

    Достоинства динамической компиляции по сравнению с компиляцией:

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

    Недостатки динамической компиляции по сравнению с компиляцией и чистой интерпретацией:

    • бо́льшая сложность реализации;
    • бо́льшие требования к ресурсам.

    Динамическая компиляция хорошо подходит для веб-приложений .

    Динамическая компиляция появилась и поддерживается в той или иной мере в реализациях Java , .NET Framework , Perl , Python .

    Смешение понятий трансляции и интерпретации

    Понятия «трансляция» и «интерпретация» отличаются. Во время трансляции выполняется преобразование кода программы с одного языка на другой. Во время интерпретации программа исполняется.

    Так как целью трансляции является, обычно, подготовка к интерпретации, эти процессы рассматриваются вместе. Например, языки программирования часто характеризуются как «компилируемые» или «интерпретируемые», в зависимости от того, что преобладает при использовании языка: компиляция или интерпретация. Причём, практически все языки низкого уровня и третьего поколения , вроде ассемблера , Си или Модулы-2 , являются компилируемыми, а более высокоуровневые языки , вроде Python или SQL - интерпретируемыми.

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

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

    Напишите отзыв о статье "Транслятор"

    Примечания

    1. ГОСТ 19781-83 // Вычислительная техника. Терминология: Справочное пособие. Выпуск 1 / Рецензент канд. техн. наук Ю. П. Селиванов. - М .: Издательство стандартов, 1989. - 168 с. - 55 000 экз. - ISBN 5-7050-0155-X .
    2. Першиков В. И., Савинков В. М. Толковый словарь по информатике / Рецензенты: канд. физ.-мат. наук А. С. Марков и д-р физ.-мат. наук И. В. Поттосин. - М .: Финансы и статистика, 1991. - 543 с. - 50 000 экз. - ISBN 5-279-00367-0 .
    3. СТ ИСО 2382/7-77 // Вычислительная техника. Терминология. Указ. соч.
    4. Толковый словарь по вычислительным системам = Dictionary of Computing / Под ред. В. Иллингуорта и др.: Пер. с англ. А. К. Белоцкого и др.; Под ред. Е. К. Масловского. - М .: Машиностроение, 1990. - 560 с. - 70 000 (доп,) экз. - ISBN 5-217-00617-X (СССР), ISBN 0-19-853913-4 (Великобритания).
    5. Органик Э. Организация системы Интел 432 = A Programmer’s View of the Intel 432 System / Пер. с англ. - М .: Мир, 1987. - С. 20, 31. - 446 с. - 59 000 экз.
    6. Роберт У. Себеста. 1.7. Методы реализации // Основные концепции языков программирования = Concepts of Programming Languages / Пер. с англ. - 5-е изд. - М .: Вильямс , 2001. - С. 45‑52. - 672 с. - 5000 экз. - ISBN 5-8459-0192-8 (рус.), ISBN 0-201-75295-6 (англ.).

    Литература

    • Касьянов В. Н., Поттосин И. В. Методы построения трансляторов. - Новосибирск: Наука, 1986. - 344 с.

    Отрывок, характеризующий Транслятор

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

    M me Schoss, ходившая к своей дочери, еще болоо увеличила страх графини рассказами о том, что она видела на Мясницкой улице в питейной конторе. Возвращаясь по улице, она не могла пройти домой от пьяной толпы народа, бушевавшей у конторы. Она взяла извозчика и объехала переулком домой; и извозчик рассказывал ей, что народ разбивал бочки в питейной конторе, что так велено.
    После обеда все домашние Ростовых с восторженной поспешностью принялись за дело укладки вещей и приготовлений к отъезду. Старый граф, вдруг принявшись за дело, всё после обеда не переставая ходил со двора в дом и обратно, бестолково крича на торопящихся людей и еще более торопя их. Петя распоряжался на дворе. Соня не знала, что делать под влиянием противоречивых приказаний графа, и совсем терялась. Люди, крича, споря и шумя, бегали по комнатам и двору. Наташа, с свойственной ей во всем страстностью, вдруг тоже принялась за дело. Сначала вмешательство ее в дело укладывания было встречено с недоверием. От нее всё ждали шутки и не хотели слушаться ее; но она с упорством и страстностью требовала себе покорности, сердилась, чуть не плакала, что ее не слушают, и, наконец, добилась того, что в нее поверили. Первый подвиг ее, стоивший ей огромных усилий и давший ей власть, была укладка ковров. У графа в доме были дорогие gobelins и персидские ковры. Когда Наташа взялась за дело, в зале стояли два ящика открытые: один почти доверху уложенный фарфором, другой с коврами. Фарфора было еще много наставлено на столах и еще всё несли из кладовой. Надо было начинать новый, третий ящик, и за ним пошли люди.
    – Соня, постой, да мы всё так уложим, – сказала Наташа.
    – Нельзя, барышня, уж пробовали, – сказал буфетчнк.
    – Нет, постой, пожалуйста. – И Наташа начала доставать из ящика завернутые в бумаги блюда и тарелки.
    – Блюда надо сюда, в ковры, – сказала она.
    – Да еще и ковры то дай бог на три ящика разложить, – сказал буфетчик.
    – Да постой, пожалуйста. – И Наташа быстро, ловко начала разбирать. – Это не надо, – говорила она про киевские тарелки, – это да, это в ковры, – говорила она про саксонские блюда.
    – Да оставь, Наташа; ну полно, мы уложим, – с упреком говорила Соня.
    – Эх, барышня! – говорил дворецкий. Но Наташа не сдалась, выкинула все вещи и быстро начала опять укладывать, решая, что плохие домашние ковры и лишнюю посуду не надо совсем брать. Когда всё было вынуто, начали опять укладывать. И действительно, выкинув почти все дешевое, то, что не стоило брать с собой, все ценное уложили в два ящика. Не закрывалась только крышка коверного ящика. Можно было вынуть немного вещей, но Наташа хотела настоять на своем. Она укладывала, перекладывала, нажимала, заставляла буфетчика и Петю, которого она увлекла за собой в дело укладыванья, нажимать крышку и сама делала отчаянные усилия.
    – Да полно, Наташа, – говорила ей Соня. – Я вижу, ты права, да вынь один верхний.
    – Не хочу, – кричала Наташа, одной рукой придерживая распустившиеся волосы по потному лицу, другой надавливая ковры. – Да жми же, Петька, жми! Васильич, нажимай! – кричала она. Ковры нажались, и крышка закрылась. Наташа, хлопая в ладоши, завизжала от радости, и слезы брызнули у ней из глаз. Но это продолжалось секунду. Тотчас же она принялась за другое дело, и уже ей вполне верили, и граф не сердился, когда ему говорили, что Наталья Ильинишна отменила его приказанье, и дворовые приходили к Наташе спрашивать: увязывать или нет подводу и довольно ли она наложена? Дело спорилось благодаря распоряжениям Наташи: оставлялись ненужные вещи и укладывались самым тесным образом самые дорогие.
    Но как ни хлопотали все люди, к поздней ночи еще не все могло быть уложено. Графиня заснула, и граф, отложив отъезд до утра, пошел спать.
    Соня, Наташа спали, не раздеваясь, в диванной. В эту ночь еще нового раненого провозили через Поварскую, и Мавра Кузминишна, стоявшая у ворот, заворотила его к Ростовым. Раненый этот, по соображениям Мавры Кузминишны, был очень значительный человек. Его везли в коляске, совершенно закрытой фартуком и с спущенным верхом. На козлах вместе с извозчиком сидел старик, почтенный камердинер. Сзади в повозке ехали доктор и два солдата.
    – Пожалуйте к нам, пожалуйте. Господа уезжают, весь дом пустой, – сказала старушка, обращаясь к старому слуге.
    – Да что, – отвечал камердинер, вздыхая, – и довезти не чаем! У нас и свой дом в Москве, да далеко, да и не живет никто.
    – К нам милости просим, у наших господ всего много, пожалуйте, – говорила Мавра Кузминишна. – А что, очень нездоровы? – прибавила она.
    Камердинер махнул рукой.
    – Не чаем довезти! У доктора спросить надо. – И камердинер сошел с козел и подошел к повозке.
    – Хорошо, – сказал доктор.
    Камердинер подошел опять к коляске, заглянул в нее, покачал головой, велел кучеру заворачивать на двор и остановился подле Мавры Кузминишны.
    – Господи Иисусе Христе! – проговорила она.
    Мавра Кузминишна предлагала внести раненого в дом.
    – Господа ничего не скажут… – говорила она. Но надо было избежать подъема на лестницу, и потому раненого внесли во флигель и положили в бывшей комнате m me Schoss. Раненый этот был князь Андрей Болконский.

    Наступил последний день Москвы. Была ясная веселая осенняя погода. Было воскресенье. Как и в обыкновенные воскресенья, благовестили к обедне во всех церквах. Никто, казалось, еще не мог понять того, что ожидает Москву.
    Только два указателя состояния общества выражали то положение, в котором была Москва: чернь, то есть сословие бедных людей, и цены на предметы. Фабричные, дворовые и мужики огромной толпой, в которую замешались чиновники, семинаристы, дворяне, в этот день рано утром вышли на Три Горы. Постояв там и не дождавшись Растопчина и убедившись в том, что Москва будет сдана, эта толпа рассыпалась по Москве, по питейным домам и трактирам. Цены в этот день тоже указывали на положение дел. Цены на оружие, на золото, на телеги и лошадей всё шли возвышаясь, а цены на бумажки и на городские вещи всё шли уменьшаясь, так что в середине дня были случаи, что дорогие товары, как сукна, извозчики вывозили исполу, а за мужицкую лошадь платили пятьсот рублей; мебель же, зеркала, бронзы отдавали даром.
    В степенном и старом доме Ростовых распадение прежних условий жизни выразилось очень слабо. В отношении людей было только то, что в ночь пропало три человека из огромной дворни; но ничего не было украдено; и в отношении цен вещей оказалось то, что тридцать подвод, пришедшие из деревень, были огромное богатство, которому многие завидовали и за которые Ростовым предлагали огромные деньги. Мало того, что за эти подводы предлагали огромные деньги, с вечера и рано утром 1 го сентября на двор к Ростовым приходили посланные денщики и слуги от раненых офицеров и притаскивались сами раненые, помещенные у Ростовых и в соседних домах, и умоляли людей Ростовых похлопотать о том, чтоб им дали подводы для выезда из Москвы. Дворецкий, к которому обращались с такими просьбами, хотя и жалел раненых, решительно отказывал, говоря, что он даже и не посмеет доложить о том графу. Как ни жалки были остающиеся раненые, было очевидно, что, отдай одну подводу, не было причины не отдать другую, все – отдать и свои экипажи. Тридцать подвод не могли спасти всех раненых, а в общем бедствии нельзя было не думать о себе и своей семье. Так думал дворецкий за своего барина.
    Проснувшись утром 1 го числа, граф Илья Андреич потихоньку вышел из спальни, чтобы не разбудить к утру только заснувшую графиню, и в своем лиловом шелковом халате вышел на крыльцо. Подводы, увязанные, стояли на дворе. У крыльца стояли экипажи. Дворецкий стоял у подъезда, разговаривая с стариком денщиком и молодым, бледным офицером с подвязанной рукой. Дворецкий, увидав графа, сделал офицеру и денщику значительный и строгий знак, чтобы они удалились.
    – Ну, что, все готово, Васильич? – сказал граф, потирая свою лысину и добродушно глядя на офицера и денщика и кивая им головой. (Граф любил новые лица.)
    – Хоть сейчас запрягать, ваше сиятельство.
    – Ну и славно, вот графиня проснется, и с богом! Вы что, господа? – обратился он к офицеру. – У меня в доме? – Офицер придвинулся ближе. Бледное лицо его вспыхнуло вдруг яркой краской.
    – Граф, сделайте одолжение, позвольте мне… ради бога… где нибудь приютиться на ваших подводах. Здесь у меня ничего с собой нет… Мне на возу… все равно… – Еще не успел договорить офицер, как денщик с той же просьбой для своего господина обратился к графу.
    – А! да, да, да, – поспешно заговорил граф. – Я очень, очень рад. Васильич, ты распорядись, ну там очистить одну или две телеги, ну там… что же… что нужно… – какими то неопределенными выражениями, что то приказывая, сказал граф. Но в то же мгновение горячее выражение благодарности офицера уже закрепило то, что он приказывал. Граф оглянулся вокруг себя: на дворе, в воротах, в окне флигеля виднелись раненые и денщики. Все они смотрели на графа и подвигались к крыльцу.
    – Пожалуйте, ваше сиятельство, в галерею: там как прикажете насчет картин? – сказал дворецкий. И граф вместе с ним вошел в дом, повторяя свое приказание о том, чтобы не отказывать раненым, которые просятся ехать.
    – Ну, что же, можно сложить что нибудь, – прибавил он тихим, таинственным голосом, как будто боясь, чтобы кто нибудь его не услышал.
    В девять часов проснулась графиня, и Матрена Тимофеевна, бывшая ее горничная, исполнявшая в отношении графини должность шефа жандармов, пришла доложить своей бывшей барышне, что Марья Карловна очень обижены и что барышниным летним платьям нельзя остаться здесь. На расспросы графини, почему m me Schoss обижена, открылось, что ее сундук сняли с подводы и все подводы развязывают – добро снимают и набирают с собой раненых, которых граф, по своей простоте, приказал забирать с собой. Графиня велела попросить к себе мужа.
    – Что это, мой друг, я слышу, вещи опять снимают?
    – Знаешь, ma chere, я вот что хотел тебе сказать… ma chere графинюшка… ко мне приходил офицер, просят, чтобы дать несколько подвод под раненых. Ведь это все дело наживное; а каково им оставаться, подумай!.. Право, у нас на дворе, сами мы их зазвали, офицеры тут есть. Знаешь, думаю, право, ma chere, вот, ma chere… пускай их свезут… куда же торопиться?.. – Граф робко сказал это, как он всегда говорил, когда дело шло о деньгах. Графиня же привыкла уж к этому тону, всегда предшествовавшему делу, разорявшему детей, как какая нибудь постройка галереи, оранжереи, устройство домашнего театра или музыки, – и привыкла, и долгом считала всегда противоборствовать тому, что выражалось этим робким тоном.
    Она приняла свой покорно плачевный вид и сказала мужу:
    – Послушай, граф, ты довел до того, что за дом ничего не дают, а теперь и все наше – детское состояние погубить хочешь. Ведь ты сам говоришь, что в доме на сто тысяч добра. Я, мой друг, не согласна и не согласна. Воля твоя! На раненых есть правительство. Они знают. Посмотри: вон напротив, у Лопухиных, еще третьего дня все дочиста вывезли. Вот как люди делают. Одни мы дураки. Пожалей хоть не меня, так детей.
    Граф замахал руками и, ничего не сказав, вышел из комнаты.
    – Папа! об чем вы это? – сказала ему Наташа, вслед за ним вошедшая в комнату матери.
    – Ни о чем! Тебе что за дело! – сердито проговорил граф.
    – Нет, я слышала, – сказала Наташа. – Отчего ж маменька не хочет?
    – Тебе что за дело? – крикнул граф. Наташа отошла к окну и задумалась.
    – Папенька, Берг к нам приехал, – сказала она, глядя в окно.

    Берг, зять Ростовых, был уже полковник с Владимиром и Анной на шее и занимал все то же покойное и приятное место помощника начальника штаба, помощника первого отделения начальника штаба второго корпуса.
    Он 1 сентября приехал из армии в Москву.
    Ему в Москве нечего было делать; но он заметил, что все из армии просились в Москву и что то там делали. Он счел тоже нужным отпроситься для домашних и семейных дел.
    Берг, в своих аккуратных дрожечках на паре сытых саврасеньких, точно таких, какие были у одного князя, подъехал к дому своего тестя. Он внимательно посмотрел во двор на подводы и, входя на крыльцо, вынул чистый носовой платок и завязал узел.
    Из передней Берг плывущим, нетерпеливым шагом вбежал в гостиную и обнял графа, поцеловал ручки у Наташи и Сони и поспешно спросил о здоровье мамаши.
    – Какое теперь здоровье? Ну, рассказывай же, – сказал граф, – что войска? Отступают или будет еще сраженье?
    – Один предвечный бог, папаша, – сказал Берг, – может решить судьбы отечества. Армия горит духом геройства, и теперь вожди, так сказать, собрались на совещание. Что будет, неизвестно. Но я вам скажу вообще, папаша, такого геройского духа, истинно древнего мужества российских войск, которое они – оно, – поправился он, – показали или выказали в этой битве 26 числа, нет никаких слов достойных, чтоб их описать… Я вам скажу, папаша (он ударил себя в грудь так же, как ударял себя один рассказывавший при нем генерал, хотя несколько поздно, потому что ударить себя в грудь надо было при слове «российское войско»), – я вам скажу откровенно, что мы, начальники, не только не должны были подгонять солдат или что нибудь такое, но мы насилу могли удерживать эти, эти… да, мужественные и древние подвиги, – сказал он скороговоркой. – Генерал Барклай до Толли жертвовал жизнью своей везде впереди войска, я вам скажу. Наш же корпус был поставлен на скате горы. Можете себе представить! – И тут Берг рассказал все, что он запомнил, из разных слышанных за это время рассказов. Наташа, не спуская взгляда, который смущал Берга, как будто отыскивая на его лице решения какого то вопроса, смотрела на него.


    4. Основные принципы построения трансляторов. Трансляторы, компиляторы и интерпретаторы – общая схема работы. Современные компиляторы и интерпретаторы.

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

    Трансляторы, компиляторы, интерпретаторы – общая схема работы.

    Определение транслятора, компилятора, интерпретатора

    Для начала дадим несколько определений - что же все-таки такое есть уже мно­гократно упоминавшиеся трансляторы и компиляторы.

    Формальное определение транслятора

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

    Во-первых, сам транслятор является программой 1 - обычно он входит в состав системного программного обеспечения вычислительной системы. То есть транс­лятор - это часть программного обеспечения (ПО), он представляет собой на­бор машинных команд и данных и выполняется компьютером, как и все прочие программы в рамках операционной системы (ОС). Все составные части трансля­тора представляют собой фрагменты или модули программы со своими входны­ми и выходными данными.

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

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

    Итак, чтобы создать транслятор, необходимо прежде всего выбрать входной и выходной языки. С точки зрения преобразования предложений входного язы­ка в эквивалентные им предложения выходного языка транслятор выступает как переводчик. Например, трансляция программы с языка С в язык ассемблера по сути ничем не отличается от перевода, скажем, с русского языка на английский, с той только разницей, что сложность языков несколько иная (почему не суще­ствует трансляторов с естественных языков - см. раздел «Классификация язы­ков и грамматик», глава 9). Поэтому и само слово «транслятор» (английское: translator) означает «переводчик».

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

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

    Определение компилятора.

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

    Кроме понятия «транслятор» широко употребляется также близкое ему по смыс­лу понятие «компилятор».

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

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

    Само слово «компилятор» происходит от английского термина «compiler» («со­ставитель», «компоновщик»). Видимо, термин обязан своему происхождению способности компиляторов составлять объектные программы на основе исход­ных программ.

    Результирующая программа компилятора называется «объектной программой» или «объектным кодом». Файл, в который она записана, обычно называется «объ­ектным файлом». Даже в том случае, когда результирующая программа порож­дается на языке машинных команд, между объектной программой (объектным файлом) и исполняемой программой (исполняемым файлом) есть существенная разница. Порожденная компилятором программа не может непосредственно выполняться на компьютере, так как она не привязана к конкретной области па­мяти, где должны располагаться ее код и данные (более подробно - см. раздел «Принципы функционирования систем программирования», глава 15) 2 .

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

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

    Определение интерпретатора. Разница между интерпретаторами и трансляторами

    Кроме схожих между собой понятий «транслятор» и «компилятор» существует принципиально отличное от них понятие интерпретатора.

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

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

    Конечно, чтобы исполнить исходную программу, интерпретатор так или иначе должен преобразовать ее в язык машинных кодов, поскольку иначе выполнение программ на компьютере невозможно. Он и делает это, однако полученные ма­шинные коды не являются доступными - их не видит пользователь интерпрета­тора. Эти машинные коды порождаются интерпретатором, исполняются и унич-

    1 Следует особо упомянуть, что сейчас в современных системах программирования стали появляться компиляторы, в которых результирующая программа создается не на языке машинных команд и не на языке ассемблера, а на некотором промежуточном языке. Сам по себе этот промежуточный язык не может непосредственно исполняться на компьюте­ре, а требует специального промежуточного интерпретатора для выполнения написан­ных на нем программ. Хотя в данном случае термин «транслятор» был бы, наверное, более правильным, в литературе употребляется понятие «компилятор», поскольку про­межуточный язык является языком очень низкого уровня, будучи родственным машин­ным командам и языкам ассемблера.

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

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

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

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

    С тех пор все развитие программного обеспечения компьютеров неразрывно i зано с возникновением и развитием компиляторов.

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

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

    Появление языков высокого уровня существенно упростило процесс програи рования, хотя и не свело его до «уровня домохозяйки», как самонадеянно п гали некоторые авторы на заре рождения языков программирования 1 . Снатаких языков были единицы, затем десятки, сейчас, наверное, их насчитывается более сотни. Процессу этому не видно конца. Тем не менее по-прежнему преоб­ладают компьютеры традиционной, «неймановской», архитектуры, которые уме­ют понимать только машинные команды, поэтому вопрос о создании компилято­ров продолжает быть актуальным.

    Как только возникла массовая потребность в создании компиляторов, стала раз­виваться и специализированная теория. Со временем она нашла практическое приложение во множестве созданных компиляторов. Компиляторы создавались и продолжают создаваться не только для новых, но и для давно известных язы­ков. Многие производители от известных, солидных фирм (таких, как Microsoft или Inprise) до мало кому знакомых коллективов авторов выпускают на рынок все новые и новые образцы компиляторов. Это обусловлено рядом причин, кото­рые будут рассмотрены далее.

    Наконец, с тех пор как большинство теоретических аспектов в области ком­пиляторов получили свою практическую реализацию (а это, надо сказать, про­изошло довольно быстро, в конце 60-х годов), развитие компиляторов пошло по пути их дружественности человеку - пользователю, разработчику программ на языках высокого уровня. Логичным завершением этого процесса стало создание систем программирования - программных комплексов, объединяющих в себе кроме непосредственно компиляторов множество связанных с ними компонен­тов программного обеспечения. Появившись, системы программирования быстро завоевали рынок и ныне в массе своей преобладают на нем (фактически, обособ­ленные компиляторы - это редкость среди современных программных средств). О том, что представляют собой и как организованы современные системы про­граммирования, см. в главе «Современные системы программирования». Ныне компиляторы являются неотъемлемой частью любой вычислительной сис­темы. Без их существования программирование любой прикладной задачи было бы затруднено, а то и просто невозможно. Да и программирование специали­зированных системных задач, как правило, ведется если не на языке высокого уровня (в этой роли в настоящее время чаще всего применяется язык С), то на языке ассемблера, следовательно, применяется соответствующий компилятор. Программирование непосредственно на языках машинных кодов происходит ис­ключительно редко и только для решения очень узких вопросов. Несколько слов о примерах реализации компиляторов и интерпретаторов, а также о том, как они соотносятся с другими существующими программными средствами. Компиляторы, как будет показано далее, обычно несколько проще в реализации, чем интерпретаторы. По эффективности они также превосходят их - очевидно, что откомпилированный код будет исполняться всегда быстрее, чем происходит интерпретация аналогичной исходной программы. Кроме того, не каждый язык программирования допускает построение простого интерпретатора. Однако ин­терпретаторы имеют одно существенное преимущество - откомпилированный код всегда привязан к архитектуре вычислительной системы, на которую он ори­ентирован, а исходная программа - только к семантике языка программирова­ния, которая гораздо легче поддается стандартизации. Этот аспект первоначаль­но не принимали во внимание. Первыми компиляторами были компиляторы с мнемокодов. Их потомки - со­временные компиляторы с языков ассемблера - существую практически для всех известных вычислительных систем. Они предельно жестко ориентированы на архитектуру. Затем появились компиляторы с таких языков, как FORTRAN, ALGOL-68, PL/1. Они были ориентированы на большие ЭВМ с пакетной обра­боткой задач. Из вышеперечисленных только FORTRAN, пожалуй, продолжает использоваться по сей день, поскольку имеет огромное количество библиотек различного назначения . Многие языки, родившись, так и не получили широ­кого распространения - ADA, Modula, Simula известны лишь узкому кругу спе­циалистов. В то же время на рынке программных систем доминируют компиля­торы языков, которым не прочили светлого будущего. В первую очередь, сейчас это С и C++. Первый из них родился вместе с операционными системами типа UNIX, вместе с нею завоевал свое «место под солнцем», а затем перешел под ОС других типов. Второй удачно воплотил в себе пример реализации идей объектно-ориентированного программирования на хорошо зарекомендовавшей себя прак­тической базе 1 . Еще можно упомянуть довольно распространенный Pascal, кото­рый неожиданно для многих вышел за рамки чисто учебного языка для универ­ситетской среды.

    История интерпретаторов не столь богата (пока!). Как уже было сказано, изна­чально им не предавали существенного значения, поскольку почти по всем пара­метрам они уступают компиляторам. Из известных языков, предполагавших интерпретацию, можно упомянуть разве что Basic, хотя большинству сейчас из­вестна его компилируемая реализация Visual Basic, сделанная фирмой Microsoft . Тем не менее сейчас ситуация несколько изменилась, поскольку вопрос о переносимости программ и их аппаратно-платформенной независимости приоб­ретает все большую актуальность с развитием сети Интернет. Самый известный сейчас пример - это язык Java (сам по себе он сочетает компиляцию и интерпре­тацию), а также связанный с ним JavaScript. В конце концов, язык HTML, на ко­тором зиждется протокол HTTP, давший толчок столь бурному развитию Все­мирной сети, - это тоже интерпретируемый язык. По мнению автора, в области появления новых интерпретаторов всех еще ждут сюрпризы, и появились уже первые из них - например, язык С# («си-диез», но название везде идет как «Си шарп»), анонсируемый фирмой Microsoft.

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

    Этапы трансляции. Общая схема работы транслятора

    На рис. 13.1 представлена общая схема работы компилятора. Из нее видно, что ъ целом процесс компиляции состоит из двух основных этапов - синтеза и анализа.

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

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

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

    Эти этапы, в свою Очередь, состоят из более мелких этапов, называемых фазами компиляции. Состав фаз компиляции приведен в самом общем виде, их конкрет­ная реализация и процесс взаимодействия

    Во-первых, он является распознавателем для языка исходной программы. То ее он должен получить на вход цепочку символов входного языка, проверить принадлежность языку и, более того, выявить правила, по которым эта цепоч была построена (поскольку сам ответ на вопрос о принадлежности «да» и. «нет» представляет мало интереса). Интересно, что генератором цепочек входи го языка выступает пользователь - автор входной программы.

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

    Лексический анализ (сканер) - это часть компилятора, которая читает лите] программы на исходном языке и строит из них слова (лексемы) исходного яз ка. На вход лексического анализатора поступает текст исходной программ а выходная информация передаётся для дальнейшей обработки компилятор на этапе синтаксического разбора. С теоретической точки зрения лексическ анализатор не является обязательной, необходимой частью компилятора. Од1 ко существует причины, которые определяют его присутствие практически всех компиляторах. Более подробно см. раздел «Лексические анализаторы (а неры). Принципы построения сканеров».

    Синтаксический разбор - это основная часть компилятора на этапе анализа. О выполняет выделение синтаксических конструкций в тексте исходной прогре мы, обработанном лексическим анализатором. На этой же фазе компиляц проверяется синтаксическая правильность программы. Синтаксический раз£ играет главную роль - роль распознавателя текста входного языка програмл рования (см. раздел «Синтаксические анализаторы. Синтаксически управл; мый перевод» этой главы).

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

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

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

    Могут, конечно, различаться в зави­симости от версии компилятора. Однако в том или ином виде все представлен­ные фазы практически всегда присутствуют в каждом конкретном компиляторе .

    Компилятор в целом с точки зрения теории формальных языков выступает в «двух ипостасях», выполняет две основные функции. программы. Это основная фаза на этапе синтеза результирующей программы. Кроме непосредственного порождения текста результирующей программы, гене­рация обычно включает в себя также оптимизацию - процесс, связанный с обра­боткой уже порожденного текста. Иногда оптимизацию выделяют в отдельную фазу компиляции, так как она оказывает существенное влияние на качество и эффективность результирующей программы (см. разделы «Генерация кода. Ме­тоды генерации кода» и « Оптимизация кода. Основные методы оптимизации», глава 14).

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

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

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

    Понятие прохода. Многопроходные и однопроходные компиляторы

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

    Реальные компиляторы, как правило, выполняют трансляцию текста исходной программы за несколько проходов.

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

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

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

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

    Однако сократить число проходов не всегда удается. Количество необходимых проходов определяется прежде всего грамматикой и семантическими правилами исходного языка. Чем сложнее грамматика языка и чем больше вариантов пред­полагают семантические правила - тем больше проходов будет выполнять ком­пилятор (конечно, играет свою роль и квалификация разработчиков компилято­ра). Например, именно поэтому обычно компиляторы с языка Pascal работают быстрее, чем компиляторы с языка С - грамматика языка Pascal более проста, а семантические правила более жесткие. Однопроходные компиляторы - редкость, они возможны только для очень про­стых языков. Реальные компиляторы выполняют, как правило, от двух до пяти проходов. Таким образом, реальные компиляторы являются многопроходными. Наиболее распространены двух- и трехпроходные компиляторы, например: пер­вый проход - лексический анализ, второй - синтаксический разбор и семанти­ческий анализ, третий - генерация и оптимизация кода (варианты исполнения, конечно, зависят от разработчика). В современных системах программирования нередко первый проход компилятора (лексический анализ кода) выполняется параллельно с редактированием кода исходной программы (такой вариант по­строения компиляторов рассмотрен далее в этой главе).

    Интерпретаторы. Особенности построения интерпретаторов

    Интерпретатор - это программа, которая воспринимает входную программу на исходном языке и выполняет ее. Как уже было сказано выше, основное отличие интерпретаторов от трансляторов и компиляторов заключается в том, что интер­претатор не порождает результирующую программу, а просто выполняет исход­ную программу.

    Термин «интерпретатор» (interpreter), как и «транслятор», означает «перевод­чик». С точки зрения терминологии эти понятия схожи, но с точки зрения тео­рии формальных языков и компиляции между ними большая принципиальная разница. Если понятия «транслятор» и «компилятор» почти неразличимы, то с понятием «интерпретатор» их путать никак нельзя.

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

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

    Отсутствие шага оптимизации определяет еще одну особенность, характерную для многих интерпретаторов: в качестве внутреннего представления программы в них очень часто используется обратная польская запись (см. раздел «Генера­ция кода. Методы генерации кода», глава 14). Эта удобная форма представления операций обладает только одним существенным недостатком - она плохо подда­ется оптимизации. Но в интерпретаторах именно это как раз и не требуется.

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

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

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

    Интерпретаторы долгое время значительно уступали в распространенности коь пиляторам. Как правило, интерпретаторы существовали для ограниченного крз га относительно простых языков программирования (таких, например, как Basic Высокопроизводительные профессиональные средства разработки программш го обеспечения строились на основе компиляторов.

    Новый импульс развитию интерпретаторов придало распространение глобал] ных вычислительных сетей. Такие сети могут включать в свой состав ЭВМ ра: личной архитектуры, и тогда требование единообразного выполнения на каждс из них текста исходной программы становится определяющим. Поэтому с разв] тием глобальных сетей и распространением всемирной сети Интернет появило
    В современных системах программирования существуют реализации программ­ного обеспечения, сочетающие в себе и функции компилятора, и функции интер­претатора - в зависимости от требований пользователя исходная программа либо компилируется, либо исполняется (интерпретируется). Кроме того, некото­рые современные языки программирования предполагают две стадии разработ­ки: сначала исходная программа компилируется в промежуточный код (некото­рый язык низкого уровня), а затем этот результат компиляции выполняется с помощью интерпретатора данного промежуточного языка. Более подробно вари­анты таких систем рассмотрены в главе «Современные системы программирова­ния».

    Широко распространенным примером интерпретируемого языка может служить HTML (Hypertext Markup Language) - язык описания гипертекста. На его осно­ве в настоящее время функционирует практически вся структура сети Интернет. Другой пример - языки Java и JavaScript - сочетают в себе функции компиля­ции и интерпретации. Текст исходной программы компилируется в некоторый промежуточный двоичный код, не зависящий от архитектуры целевой вычисли­тельной системы, этот код распространяется по сети и выполняется на прини­мающей стороне - интерпретируется.

    Трансляторы с языка ассемблера («ассемблеры»)

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

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

    Реализация компиляторов с языка ассемблера

    Язык ассемблера, как правило, содержит мнемонические коды машинных ко­манд. Чаще всего используется англоязычная мнемоника команд, но существуют и другие варианты языков ассемблера (в том числе существуют и русскоязыч­ные варианты). Именно поэтому язык ассемблера раньше носил названия «язык мнемокодов» (сейчас это название уже практически не употребляется). Все воз­можные команды в каждом языке ассемблера можно разбить на две группы: в первую группу входят обычные команды языка, которые в процессе трансля­ции преобразуются в машинные команды; вторую группу составляют специаль­ные команды языка, которые в машинные команды не преобразуются, но ис­пользуются компилятором для выполнения задач компиляции (таких, например, как задача распределения памяти). Синтаксис языка чрезвычайно прост. Команды исходной программы записыв ются обычно таким образом, чтобы на одной строке программы располагала одна команда. Каждая команда языка ассемблера, как правило, может быть рг делена на три составляющих, следующих последовательно одна за другой: по метки, код операции и поле операндов. Компилятор с языка ассемблера обыч] предусматривает и возможность наличия во входной программе комментарш которые отделяются от команд заданным разделителем .

    Поле метки содержит идентификатор, представляющий собой метку, либо явл ется пустым. Каждый идентификатор метки может встречаться в программе языке ассемблера только один раз. Метка считается описанной там, где она у посредственно встретилась в программе (предварительное описание меток требуется). Метка может быть использована для передачи управления на поь ченную ею команду. Нередко метка отделяется от остальной части команды сг циальным разделителем (чаще всего - двоеточием «:»).

    Код операции всегда представляет собой строго определенную мнемонику одн из возможных команд процессора или также строго определенную команду (мого компилятора. Код операции записывается алфавитными символами вхс ного языка. Чаще всего его длина составляет 3-4, реже - 5 или 6 символов.

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

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

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

    Например, следующая последовательность команд

    Представляет собой пример последовательности команд языка ассемблера п; цессоров семейства Intel 80x86. Здесь присутствуют команда описания наб(данных (db), метка (loops), коды операций (mov, dec и jnz). Операндами яв. ются идентификатор набора данных (datas), обозначения регистров процессе

    (Ьх и сх), метка (loops) и константа (4). Составной операнд datas отобража­ет косвенную адресацию набора данных datas по базовому регистру Ьх со смеще­нием 4.

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

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

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

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

    Во-первых, в компиляторах с языка ассемблера не выполняется дополнительная идентификация переменных - все переменные языка сохраняют имена, присво­енные им пользователем. За уникальность имен в исходной программе отвечает ее разработчик, семантика языка никаких дополнительных требований на этот процесс не накладывает. Во-вторых, в компиляторах с языка ассемблера предель­но упрощено распределение памяти. Компилятор с языка ассемблера работает только со статической памятью. Если используется динамическая память, то для работы с нею нужно использовать соответствующую библиотеку или функции ОС, а за ее распределение отвечает разработчик исходной программы. За переда­чу параметров и организацию дисплея памяти процедур и функций также отве­чает разработчик исходной программы. Он же должен позаботиться и об отделе­нии данных от кода программы - компилятор с языка ассемблера, в отличие от компиляторов с языков высокого уровня, автоматически такого разделения не выполняет. И в-третьих, на этапе генерации кода в компиляторе с языка ассемб­лера не производится оптимизация, поскольку разработчик исходной програм­мы сам отвечает за организацию вычислений, последовательность машинных ко­манд и распределение регистров процессора.

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

    Макроопределения и макрокоманды

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

    Для облегчения труда разработчика были созданы так называемые макроко­манды.

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

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

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

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

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

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

    Синтаксис макрокоманд и макроопределений не является строго заданным. Он может различаться в зависимости от реализации компилятора с языка ассембле­ра. Но сам принцип выполнения макроподстановок в тексте программы неизме­нен и не зависит от их синтаксиса.

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

    Например, следующий текст определяет макрокоманду push_0 в языке ассембле­ра процессора типа Intel 8086:

    Хог ах,ах ■ push ax endm

    Семантика этой макрокоманды заключается в записи числа «0» в стек через ре­гистр процессора ах. Тогда везде в тексте программы, где встретится макроко­манда

    Она будет заменена в результате макроподстановки на последовательность ко­манд:

    Хог ах,ах ■ push ax

    Это самый простой вариант макроопределения. Существует возможность созда­вать более сложные макроопределения с параметрами. Одно из таких макрооп­ределений описано ниже:

    Глубина такой рекурсии, как правило, сильно ограничена. На последовательность рекур­сивных вызовов макрокоманд налагаются обычно существенно более жесткие ограниче­ния, чем на последовательность рекурсивных вызовов процедур и функций, которая при стековой организации дисплея памяти ограничена только размером стека передачи пара­метров. add_abx macro xl,x2

    Push ax
    endm

    Тогда в тексте программы макрокоманда также должна быть указана с соответ­ствующим числом параметров. В данном примере макрокоманда

    Add_abx4,8 будет в результате макроподстановки заменена на последовательность команд:

    Add ах,4 add bx.4 add ex,8 push ax

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

    Loop_ax macro xl,x2,yl

    Хог bx.bx loopax: add bx.yl

    Здесь метка 1 oopax является локальной, определенной только внутри данного мак­роопределения. В этом случае уже не может быть выполнена простая текстовая подстановка макрокоманды в текст программы, поскольку если данную макро­команду выполнить дважды, то это приведет к появлению в тексте программы двух одинаковых меток 1 оорах. В таком варианте макрогенератор должен исполь­зовать более сложные методы текстовых подстановок, аналогичные тем, что ис­пользуются в компиляторах при идентификации лексических элементов вход­ной программы, чтобы дать всем возможным локальным переменным и меткам макрокоманд уникальные имена в пределах всей программы. Макроопределения и макрокоманды нашли применение не только в языках ас­семблера, но и во многих языках высокого уровня. Там их обрабатывает специ­альный модуль, называемый препроцессором языка (например, широко известен препроцессор языка С). Принцип обработки остается тем же самым, что и для программ на языке ассемблера - препроцессор выполняет текстовые подстанов­ки непосредственно над строками самой исходной программы. В языках высокого уровня макроопределения должны быть отделены от текста самой исходной программы, чтобы препроцессор не мог спутать их с синтаксиче­скими конструкциями входного языка. Для этого используются либо специаль­ные символы и команды (команды препроцессора), которые никогда не могут встречаться в тексте исходной программы, либо макроопределения встречаются

    Внутри незначащей части исходной программы - входят в состав комментариев (такая реализация существует, например, в компиляторе с языка Pascal, создан­ном фирмой Borland). Макрокоманды, напротив, могут встречаться в произволь­ном месте исходного текста программы, и синтаксически их вызов может не от­личаться от вызова функций во входном языке.

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

    Рассмотрим пример на языке С. Если описана функция

    Int fKint a) { return a + а: } и аналогичная ей макрокоманда

    #define f2(a) ((a) + (а)) то результат их вызова не всегда будет одинаков.

    Действительно, вызовы j=fl(i) и j=f2(i) (где i и j - некоторые целочисленные переменные) приведут к одному и тому же результату. Но вызовы j=fl(++i) и j=f2(++i) дадут разные значения переменной j. Дело в том, что поскольку f2 - это макроопределение, то во втором случае будет выполнена текстовая подста­новка, которая приведет к последовательности операторов j=((++i) + (++i)). Видно, что в этой последовательности операция ++i будет выполнена дважды, в отличие от вызова функции fl(++i), где она выполняется только один раз.

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

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

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

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

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

    26. Защита данных. Помехоустойчивое кодирование.

    Для обнаружения и исправления ошибок в сотовых системах связи применяется помехоустойчивое кодирование. Суть его заключается в том, что в передаваемый цифровой поток вносится некоторая избыточность. Обычно помехоустойчивое кодирование разделено на 2 части: обнаружение и исправление ошибок. Для обнаружения ошибок обычно применяется CRC (Cyclic Redundancy Check). Он реализуется по средствам вычисления контрольной суммы блока информации и передачи ее вместе с полезной информации. Причем в зависимости от степени важности и скорости передачи информации контрольная сумма может содержать больше или меньше бит. Чем выше важность информации и скорость передачи данных, тем больше контрольных бит нужно передавать. Кроме CRC в различных стандартах может применяться и другой вид кодирования.

    27. Компьютерные вирусы. Защита.

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

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

      Эвристический модуль – для выявления неизвестных вирусов

      Монитор – программа, которая постоянно находится в оперативной памяти ПК

      Устройство управления, которое осуществляет запуск антивирусных программ и обновление вирусной базы данных и компонентов

      Почтовая программа (проверяет электронную почту)

      Программа сканер – проверяет, обнаруживает и удаляет фиксированный набор известных вирусов в памяти, файлах и системных областях дисков

      Сетевой экран – защита от хакерских атак

    К наиболее эффективным и популярным антивирусным программам относятся: Антивирус Касперского 7.0, AVAST, Norton AntiVirus и многие другие.