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

Транслятор: основные понятия

Такая программа как транслятор представляет собой лингвистическое представление вычислений I ->P ->P (i). Интерпретатор представляет собой программу, на вход которой подается программа P с некоторыми входными данными X.Выполняет он P на X: I(P, x)=P(x).Существует единственный транслятор, который способен выполнять все возможные программы (которые можно представить в формальной системе). Это является очень значительным и глубоким открытием Тьюринга. Процессор представляет собой интерпретатор программ на машинном языке. Писать интерпретаторы для языков высокого уровня, как правило, слишком дорого, поэтому их транслируют в ту форму, которую легче интерпретировать. Некоторые виды трансляторов обладают очень странными именами. Программа транслирует программы на ассемблере в машинный язык. Компилятор позволяет транслировать с языка высокого уровня на язык более низкого уровня. Транслятор представляет собой программу, которая в качестве входных данных принимает программу на некотором языке S и после обработки выдает программу на языке T.Таким образом, они обе имеют ту же семантику: P->X->Q. Таким образом, для любого xP(x)=Q(x). Если транслировать всю программу в нечто интерпретируемое, то это называется компиляцией перед исполнением или компиляцией AOT. Компиляторы AOT могут использоваться последовательно. Последний из них очень часто является ассемблером. Так, рассмотрим пример: Исходный код ->Компилятор (транслятор) -> Ассемблерный код -> Ассемблер (транслятор) -> Машинный код -> ЦПУ (интерпретатор). Динамическая или оперативная компиляция осуществляется в том случае, если часть программы транслируется, когда исполняются другие скомпилированные ранее части. Трансляторы JIT запоминают то, что они уже выполнили ранее, чтобы снова и снова не повторять исходный код. Они даже способны выполнять адаптивную компиляцию и перекомпиляцию, которая основана на поведении среды выполнения программы. Многие языки дают возможность выполнять код во время трансляции, а также компилировать новый код во время выполнения программы.

Трансляция: этапы

Процесс трансляции состоит из этапов синтеза и анализа. Схематично этот процесс выглядит примерно следующим образом: Исходный код -> Анализатор -> Концептуальное представление -> Синтезатор (генератор) -> Целевой код. Обусловлено это следующими причинами:

— любой другой способ просто не подходит;

— перевод по словам просто не работает.

Можно использовать следующее инженерное решение: если необходимо написать трансляторы для M исходных языков и N целевых, потребуется написать только M+N простых программ (полукомпиляторов), а не MxN полных (комплексных) трансляторов. На практике, тем не менее, концептуальное представление довольно редко бывает выразительным и мощным, чтобы охватить все существующие целевые и исходные языки. Хотя некоторые пользователи смогли приблизиться к этому. Реальные компиляторы проходят через множество различных этапов. При создании собственного компилятора не нужно будет заново проводить всю тяжелую работу, которую программисты уже проделали при создании генераторов и представлений. Свой язык можно транслировать непосредственно в JavaScript или C и использовать для этой цели существующие компиляторы языка C и JavaScript движки для того, чтобы сделать все остальное. Можно также использовать существующие промежуточные представления и виртуальные машины.

Запись транслятора

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

  1. Транслятор – это самокомпилятор, если исходный язык у него соответствует базисному.
  2. Саморезидентным называется компилятор, у которого целевой язык равняется базисному.
  3. Если целевой и базисный языки различные, то транслятор – это кросс-компилятор.

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

Масштабная технология

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

Проектирование компилятора. Возможные проблемы, возникающие при создании реального транслятора

Какие проблемы могут возникать с исходным языком? Легко ли его скомпилировать? Имеется ли для этого препроцессор? Каким образом обрабатываются типы? Какая группировка проходов компилятора используется – одно- или многоходовая? Также особого внимания заслуживает желаемая степень оптимизации. Быстрая и нечистая трансляция программы практически без оптимизации может быть нормальной. Чрезмерная оптимизация может тормозить компилятор, однако, во время выполнения лучший код может того стоить.

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

Необходимый набор инструментов

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

Что касается вида целевого кода для генерации, тут необходимо выбирать из чистого, дополненного или виртуального машинного кода. Можно также написать входную часть, которая создает популярные промежуточные представления, такие как LLVM, JVM, RTL. Можно также сделать трансляцию из исходного в исходный код на Java Script или C. Если говорить о формате целевого кода, тут здесь можно выбрать переносимый машинный код, машинный код образа памяти, язык ассемблера.

Перенацеливание

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

Компоненты компилятора

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

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

— синтаксический анализатор (парсер) строит из них абстрактное синтаксическое дерево;

— семантический анализатор раскладывает семантическую информацию и проверяет на предмет наличия ошибок узлы дерева;

— в результате строится семантический граф. Под этим термином понимают абстрактное синтаксическое дерево с установленными ссылками и дополнительными свойствами;

— генератор промежуточного кода строит граф потока (кортежи группируются в основные блоки);

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

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

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

— используются подсистемы обнаружения ошибок и менеджер таблиц символов;

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

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

— отсутствующие в алфавите символы;

— превышение количества знаков в строке или слове;

— не закрытый строковый литерал или знак;

— конец файла в комментарии.

Синтаксический анализ или парсинг применяется для преобразования последовательности токенов в абстрактное синтаксическое дерево. При этом каждый узел дерева сохраняется как объект с именованными полями. Многие из них сами являются узлами дерева. Циклы на этом этапе отсутствуют. При создании парсера нужно в первую очередь обращать внимание на уровень сложности грамматики (LRили LL) и выяснить, имеются ли какие-то правила снятия неоднозначности. Действительно некоторые языки требуют проведения семантического анализа. Ошибки, которые встречаются на данном этапе, называются синтаксическими.

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

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

— множественные объявления переменной в пределах области ее действия;

— нарушение правил доступности;

— наличие ссылок на необъявленное имя;

— чересчур большое или, наоборот, недостаточное число аргументов при вызове метода;

— несоответствие типов.

Генерация

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

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

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

Интерпретатор (англ. interpreter - истолкователь, устный переводчик) переводит и выполняет программу строка за строкой .

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

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

Что такое системы программирования?

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

· компилятор или интерпретатор;

· интегрированная среда разработки;

· средства создания и редактирования текстов программ;

· обширные библиотеки стандартных программ и функций;

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

· многооконный режим работы;

· мощные графические библиотеки; утилиты для работы с библиотеками

· встроенный ассемблер;

· встроенная справочная служба;

· другие специфические особенности.

Популярные системы программирования – Turbo Basic , Quick Basic , Turbo Pascal , Turbo C .

В последнее время получили распространение системы программирования, ориентированные на создание Windows-приложений :

Borland Delphi 3.0

· пакет Borland Delphi (Дельфи) - блестящий наследник семейства компиляторов Borland Pascal, предоставляющий качественные и очень удобные средства визуальной разработки. Его исключительно быстрый компилятор позволяет эффективно и быстро решать практически любые задачи прикладного программирования.

· пакет Microsoft Visual Basic - удобный и популярный инструмент для создания Windows-программ с использованием визуальных средств. Содержит инструментарий для создания диаграмм и презентаций .

· пакет Borland C++ - одно из самых распространённых средств для разработки DOS и Windows приложений.

Ниже для иллюстрации приведены на языках Бейсик, Паскаль и Си программы решения одной и той же простой задачи - вычисления суммы S элементов одномерного массива A=(a 1 , a 2 , ..., a n).

Для чего нужны инструментальные программы?

По своему назначению они близки системам программирования. К инструментальным программам, например, относятся:

· редакторы;

· средства компоновки программ;

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

· вспомогательные программы, реализующие часто используемые системные действия;

· графические пакеты программ и т.п.

Инструментальные программные средства могут оказать помощь на всех стадиях разработки ПО.

Что такое текстовый редактор?

Этими данными могут быть программа или какой-либо документ или же книга. Редактируемый текст выводится на экран, и пользователь может в диалоговом режиме вносить в него свои изменения.

Текстовые редакторы могут обеспечивать выполнение разнообразных функций, а именно:

· редактирование строк текста;

· возможность использования различных шрифтов символов;

· копирование и перенос части текста с одного места на другое или из одного документа в другой;

· контекстный поиск и замена частей текста;

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

· автоматический перенос слов на новую строку;

· автоматическая нумерацию страниц;

· обработка и нумерация сносок;

· выравнивание краев абзаца;

· создание таблиц и построение диаграмм;

· проверка правописания слов и подбор синонимов;

· построение оглавлений и предметных указателей;

· распечатка подготовленного текста на принтере в нужном числе экземпляров и т.п.

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

Рис. 6.5. Окно редактора Microsoft Word

Наиболее известный текстовый редактор - Microsoft Word .

Полнофункциональные издательские системы - Microsoft Publisher , Corel Ventura и Adobe PageMaker . Издательские системы незаменимы для компьютерной верстки и графики. Значительно облегчают работу с многостраничными документами, имеют возможности автоматической разбивки текста на страницы, расстановки номеров страниц, создания заголовков и т.д. Создание макетов любых изданий - от рекламных листков до многостраничных книг и журналов - становится очень простым, даже для новичков.

Конкретными исполнителями языков программирования являются трансляторы и интерпретаторы.

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

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

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

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

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

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

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

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

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

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

Интерпретатор (англ. interpreter - истолкователь, устный переводчик) переводит и выполняет программу строка за строкой.

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

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

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

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

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

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

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

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

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

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

Алгоритм работы простого интерпретатора:

2. проанализировать инструкцию и определить соответствующие действия;

3. выполнить соответствующие действия;

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

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

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

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

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

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

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

Реализации

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

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

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

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

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

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

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

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

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

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

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

Практически во всех трансляторах (и в компиляторах, и в интерпретаторах) в том или ином виде присутствует большая часть перечисленных ниже процессов: лексический анализ; синтаксический анализ; семантический анализ; генерация внутреннего представления программы; оптимизация; генерация объектной программы. Транслятор - это программа, которая переводит входную программу на исход­ном (входном) языке в эквивалентную ей выходную программу на результирую­щем (выходном) языке. В работе транслятора уча­ствуют всегда три программы: 1) сам транслятор является программой обычно он входит в состав системного по вычислительной системы. То есть транс­лятор - это часть по. Он представляет собой на­бор машинных команд и данных и выполняется компьютером, как и все прочие программы в рамках ос. 2)исходными данными для работы транслятора служит текст входной программы - некоторая последовательность предложений входного языка про­граммирования. Этот файл должен содержать текст программы, удовлетворяющий синтаксическим и семантическим требова­ниям входного языка. 3)выходными данными транслятора является текст результирующей программы. Результирующая программа строится по синтаксическим правилам, заданным в выходном языке транслятора, а ее смысл определяется семантикой выходного языка. Важным требованием в определении транслятора является эк­вивалентность входной и выходной программ т.е совпадение их смысла с точки зрения семантики входного языка (для исходной программы) и семантики выходного языка (для результирующей про­граммы). Чтобы создать транслятор, необходимо, прежде всего, выбрать входной и выходной языки. С точки зрения преобразования предложений входного язы­ка в эквивалентные им предложения выходного языка транслятор выступает как переводчик. Результатом работы транслятора будет результирующая программа в том случае, если текст исходной программы является правильным - не со­держит ошибок с точки зрения синтаксиса и семантики входного языка. Если исходная программа неправильная, то резуль­татом работы транслятора будет сообщение об ошибке. Кроме понятия «транслятор» широко употребляется также близкое ему по смыс­лу понятие «компилятор». Компилятор - это транслятор, который осуществляет перевод исходной програм­мы в эквивалентную ей объектную программу на языке машинных команд или на языке ассемблера.т.о. Компилятор отличается от транслятора лишь тем, что его ре­зультирующая программа всегда должна быть написана на языке машинных ко­дов или на языке ассемблера. Результирующая программа компилятора называется «объектной программой» или «объектным кодом» . Файл, в который она записана, обычно называется «объ­ектным файлом» . Порожденная компилятором программа не может непосредственно выполняться на компьютере, так как она не привязана к конкретной области па­мяти, где должны располагаться ее код и данные..компиляторы, безусловно, самый распространенный вид трансляторов. Они име­ют самое широкое практическое применение, которым обязаны широкому рас­пространению всевозможных языков программирования. Сейчас в современных системах программирования стали появляться компиляторы, в которых результирующая программа создается не на языке машинных команд и не на языке ассемблера, а на некотором промежуточном языке. Он не может непосредственно исполняться на компьюте­ре, а требует специального промежуточного интерпретатора, для выполнения написан­ных на нем программ.интерпретатор - это программа, которая воспринимает входную программу на исходном языке и выполняет ее.в отличие от трансляторов интерпретаторы не порождают результирующую про­грамму - и в этом принципиаль­ная разница между ними. Интерпретатор, так же как и транслятор, анализирует текст исходной программы. Но он не порождает результирующей программы, а сразу же выполняет исходную в соответствии с ее смыслом, заданным семанти­кой входного языка. Т.о, результатом работы интерпретатора будет некоторый желаемый рез-т(если программа правильна) или сообщение об ошибке. Чтобы исполнить исходную программу, интерпретатор должен преобразовать ее в язык машинных кодов. Полученные ма­шинные коды не доступны пользователю. Они порождаются интер-ом, исполняются и уничтожаются по мере надобности. Пользователь видит результат выполнения этих кодов - т.е результат выполнения исходной программы.


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

Первыми компиляторами были компиляторы с языков ассемблера или, как они назывались, мнемокодов. Мнемокоды превратили текст программы, написанный на языке машинных команд в более-менее доступный пониманию специалиста язык. Соз­давать программы стало значительно проще, но исполнять сам мнемокод ни один компьютер неспособен, соответственно, возникла не­обходимость в создании компиляторов. Следующим этапом стало создание языков высокого уровня. Они представля­ют собой промежуточное звено между чисто формальными языками и языками естественного общения людей. От первых им досталась строгая фор­мализация синтаксических структуру предложений языка, от вторых - значи­тельная часть словарного запаса, семантика основных конструкций и выражений. Появление языков высокого уровня существенно упростило процесс программи­рования. Однако преобладают компьютеры традиционной, архитектуры, которые уме­ют понимать только машинные команды, поэтому вопрос о создании компилято­ров продолжает быть актуальным. Компиляторы создавались и продолжают создаваться не только для новых, но и для давно известных язы­ков. С тех пор как большинство теоретических аспектов в области ком­пиляторов получили свою практическую реализацию (это про­изошло в конце 60-х годов), развитие компиляторов пошло по пути их дружественности пользователю, разработчику программ на языках высокого уровня. Логичным завершением этого процесса стало создание систем программирования - программных комплексов, объединяющих в себе кроме непосредственно компиляторов множество связанных с ними компонен­тов по.на сегодняшний день компиляторы являются неотъемлемой частью любой вычислительной системы. Без их существования программирование любой прикладной задачи было бы затруднено, а то и просто невозможно. Да и программирование специализированных системных задач, как правило, ведется если не на языке высокого уровня, то на ассемблере, следовательно, применяется соответствующий компилятор. Компиляторы обычно несколько проще в реализации, чем интерпретаторы. По эффективности они также превосходят их - очевидно, что откомпилированный код будет исполняться всегда быстрее, чем происходит интерпретация аналогичной исходной программы. Кроме того, не каждый язык программирования допускает построение простого интерпретатора. Однако, интерпретаторы имеют одно существенное преимущество - откомпилированный код всегда привязан к архитектуре вычислительной системы, на которую он ори­ентирован, а исходная программа - только к семантике языка программирования, которая гораздо легче поддается стандартизации. Первыми компиляторами были компиляторы с мнемокодов. Их потомки - со­временные компиляторы с языков ассемблера - существую практически для всех известных вычислительных систем. Они предельно жестко ориентированы на архитектуру. Затем появились компиляторы с таких языков, как fortran, algol-68,. Они были ориентированы на большие эвм с пакетной обра­боткой задач. Из вышеперечисленных языков, только fortran продолжает использоваться по сей день, поскольку имеет огромное количество библиотек различного назначения. На рынке программных систем доминируют компиля­торы языков с и c++. Первый из них родился вместе с операционными системами типа unix, а затем перешел под ос других типов. Второй удачно воплотил в себе пример реализации идей объектно-ориентированного программирования на хорошо зарекомендовавшей себя прак­тической базе. Изна­чально интерпретаторам не предавали существенного значения, поскольку почти по всем пара­метрам они уступают компиляторам. Тем не менее сейчас ситуация несколько изменилась, поскольку вопрос о переносимости программ и их аппаратно-платформенной независимости приоб­ретает все большую актуальность с развитием сети интернет. Самый известный сейчас пример - это язык java (сам по себе он сочетает компиляцию и интерпре­тацию), а также связанный с ним javascript. Кроме того, язык html, на ко­тором зиждется протокол http - это тоже интерпретируемый язык.

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

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

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

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

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

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

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

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

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

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

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

Интерпретатор - это программа, которая воспринимает входную программу на исходном языке и выполняет ее. Основное отличие интерпретаторов от трансляторов и компиляторов заключается в том, что интер­претатор не порождает результирующую программу, а просто выполняет исход­ную программу. Термин «интерпретатор» (interpreter) означает «перевод­чик» . Простейшим способом реализации интерпретатора можно было бы считать ва­риант, когда исходная программа сначала полностью транслируется в машинные команды, а затем сразу же выполняется. В такой реализации интерпретатор, мало бы, чем отличался от компилятора с той лишь разницей, что результи­рующая программа в нем была бы недоступна пользователю. Недостатком тако­го интерпретатора было бы то, что пользователь должен был бы ждать компиля­ции всей исходной программы прежде, чем начнется ее выполнение. По сути, в таком интерпретаторе не было бы никакого особого смысла - он не давал бы никаких преимуществ по сравнению с аналогичным компилятором. Поэтому подавляющее большинство интерпретаторов действует так, что испол­няет исходную программу последовательно, по мере ее поступления на вход ин­терпретатора. Тогда пользователю не надо ждать завершения компиляции всей исходной программы. Более того, он может последовательно вводить исходную программу и тут же наблюдать результат ее выполнения по мере ввода команд. При таком порядке работы интерпретатора проявляется существенная особен­ность, которая отличает его от компилятора, - если интерпретатор исполняет команды по мере их поступления, то он не может выполнять оптимизацию ис­ходной программы. Следовательно, фаза оптимизации в общей структуре интерпретатора будет отсутствовать. В остальном же она будет мало отличаться от структуры аналогичного компилятора. Далеко не все языки программирования допускают построение интерпретаторов, которые могли бы выполнять исходную программу по мере поступления команд. Для этого язык должен допускать возможность существования компилятора, выполняющего разбор исходной программы за один проход. Кроме того, язык не может интерпретироваться по мере поступления команд, если он допускает по­явление обращений к функциям и структурам данных раньше их непосредствен­ного описания. Отсутствие шага оптимизации ведет к тому, что выполнение программы с помо­щью интерпретатора является менее эффективным, чем с помощью аналогично­го компилятора. Т.о, интерпретаторы всегда проигрывают компиляторам в про­изводительности. Преимуществом интерпретатора является независимость выполнения програм­мы от архитектуры целевой вычислительной системы. В результате компиляции получается объектный код, который всегда ориентирован на определенную архи­тектуру. Для перехода на другую архитектуру целевой вычислительной системы программу требуется откомпилировать заново. А для интерпретации программы необходимо иметь только ее исходный текст и интерпретатор с соответствующе­го языка. Интерпретаторы существовали для ограниченного кру­га относительно простых языков программирования (basic). Высокопроизводительные профессиональные средства разработки программно­го обеспечения строились на основе компиляторов. Новый импульс развитию интерпретаторов придало распространение глобаль­ных вычислительных сетей. Такие сети могут включать в свой состав эвм раз­личной архитектуры, и тогда требование единообразного выполнения на каждой из них текста исходной программы становится определяющим. Поэтому с разви­тием глобальных сетей и распространением всемирной сети интернет появилось много новых систем, интерпретирующих текст исходной программы. В современных системах программирования существуют реализации по, сочетающие в себе и функции компилятора, и функции интер­претатора - в зависимости от требований пользователя исходная программа либо компилируется, либо исполняется (интерпретируется). Некото­рые современные языки программирования предполагают две стадии разработ­ки: сначала исходная программа компилируется в промежуточный код, а затем этот результат компиляции выполняется с помощью интерпретатора данного промежуточного языка. Примером интерпретируемого языка может служить html (hypertext markup language) - язык описания гипертекста или языки java и javascript - сочетают в себе функции компиля­ции и интерпретации.